Отказоустойчивое взаимодействие с медленным внешним сервисом?
Доброго времени суток.
Есть некий сервис, постоянно взаимодействующий с внешним сервисом (далее агрегатор).
Порядок взаимодействия примерно таков:
Сайт (laravel) -> api (laravel) (для общения с агрегатором) -> агрегатор* -> поставщик*
*приложения на работу которых нельзя влиять.
Согласно схеме, например, что бы нам получить и отобразить на сайте Сущность А от Сервиса А, сайт обращается к апи, апи в свою очередь к агрегатору, и уже поставщик Сервиса А отдает Сущность А.
Проблема заключается в том, что время ответа агрегатора может доходить до 30-40 секунд и в пики посещаемости сайт падает по причине огромного кол-ва php-fpm процессов.
Вопрос заключается в том, как построить отказоустойчивое взаимодействие, при условии что:
1. Не имеет никакого смысла делать попытки кеширования/хранения Сущностей, так как:
1.1. Сущности могут быть изменены извне и нам необходимо это видеть.
1.2. Даже при кешировании Сущности А, останутся запросы к Сущностям XXX которые будут обращаться к медленному Сервису А.
2. Нельзя уменьшать время тайм-аута
Вертикальное/горизонтальное масштабирование результатов не приносит, увеличение кол-ва доступных php-fpm лишь немного отодвигает момент падения.
Единственный вариант приходящий на ум, вынести взаимодействие с агрегатором в очередь. Но, в таком случае возрастает сложность получения данных на фронте.
Было бы интересно выслушать похожий опыт решения проблем или ссылки на статьи.
если что-то не может вам отдавать данные с той скоростью что вам нужно, а кешировать нельзя - решений вообщем то нет. Можете загнать в очередь, в бд или еще чего, ну можно сэкономить на дублях обращений. Но это такое.
Если труба выдает 1 литр в день, а вам надо 200 литров - то финита ля комедия, нужно еще 199 труб таких
увеличение кол-ва доступных php-fpm лишь немного отодвигает момент падения.
Из-за чего происходит падение? И что падает?
При недостатке воркеров задачи должны становиться в очередь.
Если тупо в лоб решать, fpm воркеров вам нужно не бесконечно много, а ровно столько, чтобы компенсировать лаг ответа. Если они ничего не делают, почему бы их не сделать много ровно в том пуле что отвечает за долгие запросы. Упираетесь в память?
Нужно класть задание в БД, получать в фоне, класть в БД ответ, затем на клиент прокидывать через пушер/аналог или если лень его настраивать - то с клиента раз в 5 секунд дергать "уже готово? а сейчас?"
Если обобщить, fpm процесс кладёт задание в очередь. По мере обработки очереди ответ поступает на фронт.
Конкретная реализация зависит от ваших хотелок. Очередь в бд или в брокере. Polling на фронте или websocket.
Обработку очереди лучше сделать асинхронно, иначе опять наплодите процессы в ожидании.
Судя по всему, вы упёрлись в проблему ввода/вывода. И для её решения вы решили запустить побольше параллельных процессов. Однако, эти процессы ничего не делают кроме ожидания, поэтому тратят ресурсы впустую.
Обычно в таких случаях используют умную очередь сообщений. В большинстве языков это называется асинхронными потоками или просто async/await. Т.е. вы запрашиваете какие-то данные как await long_function(), и планировщик ресурсов просто прерывает выполнение процесса до получения результата.
С другой стороны, достаточно добавить в каждый процесс вызов системного ожидания вроде sleep(0.1), что позволит системному планировщику процессов также остановить текущий процесс на несколько тактов подряд.
Чем больше знаю, тем лучше понимаю, как мало знаю.
Урезоньте алгоритм, генерирующий безлимитно эти php-fpm процессы. Научите скрипт обработки прорабатывать несколько запросов, последовательно или параллельно (и да не забанит вас внешний сервис).
Тут только очереди, чтобы вся "тяжелая" работа делалалась в фоне.
А скрипты куда обращаются клиенты будут отрабатывать максимально быстро: данные есть - отдать, данных нету - создать задачу в очереди, данные подготавливаются - сообщить об этом.