Как выстраивать взаимодействие микросервисов: через очередь или прямые вызовы?
Что проверяют
Понимаете ли вы, что выбор определяется тем, нужен ли вызывающему ответ прямо сейчас, а не модой на брокеры. Следом спрашивают, как вы переживёте дубли сообщений и падение соседнего сервиса.
Как отвечать
Начните с критерия, потом назовите цену каждого варианта:
- синхронный вызов (HTTP через
httpxилиaiohttp, gRPC черезgrpcio): когда без ответа нельзя продолжить, например проверить остаток перед списанием. Минус во временной связанности: сервисы должны быть живы одновременно, и доступность цепочки перемножается. Три сервиса по 99,9% дают около 99,7% на весь запрос; - обвязка синхронного вызова: таймауты, повторы с экспоненциальной задержкой и джиттером, circuit breaker. В
requestsтаймаута по умолчанию нет, запрос может висеть бесконечно, уhttpxон 5 секунд; - очередь или лог событий (RabbitMQ через
aio-pika, Kafka черезaiokafka, фоновые задачи на Celery): когда вызывающему достаточно знать, что работа принята. Письма, пересчёты, медленные внешние интеграции, пики нагрузки, которые брокер сглаживает; - цена асинхронности: доставка почти всегда at-least-once, значит потребитель обязан быть идемпотентным. Порядок в Kafka гарантирован только внутри партиции, для необрабатываемых сообщений нужна dead letter очередь, а данные сходятся не сразу, а в конечном счёте;
- двойная запись: сохранить заказ и опубликовать событие одной транзакцией нельзя. Решение это transactional outbox: событие пишется в таблицу той же транзакцией, отдельный процесс публикует его в брокер.
Итог: в живой системе оба способа работают рядом. Запрос пользователя идёт синхронно, побочные эффекты уходят событиями.
Где спотыкаются
Гонят через очередь то, где пользователь ждёт ответа, и строят request-reply поверх RabbitMQ. Получается медленный и хрупкий RPC с минусами обоих подходов.
Не думают про дубли. Брокер повторит сообщение после сбоя, и без ключа идемпотентности клиент получит два письма или два списания.
Считают синхронные вызовы простыми и не ставят таймауты. Один подвисший сервис съедает пул соединений и воркеров, и отказ каскадом идёт по всей цепочке.