В чем разница между синхронной интеграцией через HTTP и асинхронной через message broker
Что проверяют
Понимаете ли вы, какую связанность между сервисами создаёт каждый способ и чем за неё платите. Следом почти всегда спрашивают, как не потерять сообщение, если сервис упал между записью в базу и публикацией, и что делать с дублями.
Как отвечать
Суть различия во временной связанности. При HTTP вызывающий сервис ждёт ответа, и оба должны быть живы одновременно. С брокером отправитель кладёт сообщение и идёт дальше, а получатель обработает его, когда сможет.
- HTTP (или gRPC): ответ нужен сразу, например проверить остаток перед оформлением заказа. В .NET это
HttpClientчерезIHttpClientFactoryплюс политики устойчивости: таймаут, повтор, circuit breaker (Polly илиMicrosoft.Extensions.Http.Resilience). Минус: задержки в цепочке складываются, а падение одного сервиса каскадом валит вызывающих; - брокер (RabbitMQ, Kafka, Azure Service Bus): фоновая и долгая работа, рассылка события нескольким подписчикам, сглаживание пиков нагрузки. Получатель может лежать, сообщения его дождутся;
- цена брокера: доставка обычно «хотя бы один раз», поэтому обработчик обязан быть идемпотентным. Появляется итоговая согласованность, вопрос порядка сообщений, очередь недоставленных (DLQ) и отдельная инфраструктура;
- Transactional Outbox: сообщение пишется в таблицу в той же транзакции, что и бизнес-данные, а отдельный процесс публикует его в брокер. Так не бывает ситуации «заказ сохранён, событие потеряно».
Из библиотек стоит назвать MassTransit или Wolverine: они берут на себя повторы, outbox и маршрутизацию поверх транспорта.
Где спотыкаются
Путают async/await с асинхронной интеграцией. Асинхронный HTTP-вызов не блокирует поток, но сервис всё равно ждёт ответа и зависит от доступности соседа.
Считают, что брокер гарантирует доставку ровно один раз. Без идемпотентности повторная доставка спишет деньги дважды.
Вызывают SaveChanges, а потом Publish без outbox. Падение процесса между ними оставляет систему в рассогласованном состоянии.