Транспорт бэкенда
Брокеры сообщений: зачем и когда
Amazon Prime Day 2023: в пике нагрузки через их системы проходило 84 000 заказов в минуту. Ни один HTTP-запрос напрямую от frontend к warehouse не пережил бы эту нагрузку - слишком разные скорости, слишком много точек отказа. Брокеры сообщений - это то, что позволяет системам на разных скоростях работать как единое целое.
- **Uber** обрабатывает 1+ миллион событий геолокации в секунду через Kafka - GPS-координаты водителей публикуются в топики, десятки downstream-сервисов подписаны на обновления
- **WhatsApp** доставляет 100 млрд сообщений в день через систему, построенную на очередях с at-least-once семантикой и idempotency на стороне приёма
- **Shopify** в Black Friday переключает платёжный pipeline на async через брокер: покупатель видит 'обрабатывается' мгновенно, фактическое списание происходит через очередь с retry при сбоях банка
Зачем нужны брокеры
В 2017 году Knight Capital потеряла USD 440 миллионов за 45 минут из-за прямых синхронных вызовов между торговыми системами - один сбой каскадно уронил всё. Брокер сообщений существует именно чтобы разорвать этот каскад: производитель (producer) кладёт сообщение в брокер и не ждёт, пока потребитель (consumer) его обработает. Если consumer упал - сообщение никуда не исчезло. Если producer генерирует в 10 раз больше, чем consumer успевает обрабатывать - брокер поглощает пики.
Брокер решает три фундаментальные проблемы: **Temporal decoupling** - producer и consumer не обязаны работать одновременно. **Rate decoupling** - разные скорости генерации и обработки. **Space decoupling** - producer не знает, кто и где обрабатывает сообщения. Без брокера все три проблемы решаются вручную в каждом сервисе - и решаются плохо.
Какую из следующих проблем брокер сообщений НЕ решает?
Очереди vs Топики
Очередь (queue) и топик (topic/pub-sub) решают принципиально разные задачи, хотя оба хранят сообщения. Очередь - это работа: сообщение должно обработать ровно одно из N worker'ов и удалить. Топик - это событие: N подписчиков должны получить копию каждого сообщения независимо. Конкретный пример: заказ идёт в очередь (один склад обрабатывает, не все сразу), а событие 'пользователь зарегистрировался' идёт в топик (email-сервис, CRM и аналитика получают копии параллельно).
**Очередь (Queue / Point-to-Point)**: один producer, один consumer на каждое сообщение; после успешной обработки сообщение удаляется; несколько worker'ов конкурируют за сообщения (competing consumers). **Топик (Topic / Pub-Sub)**: один или несколько producer'ов; каждый subscriber получает полную копию; сообщение не удаляется до тех пор, пока все subscribers не прочитали (или истёк TTL). RabbitMQ реализует и то, и другое через разные типы exchanges. Kafka - это всегда топик с retention.
Сервис обрабатывает платежи: каждый платёж должен обработать ровно одна копия сервиса. Какая модель подходит?
Гарантии доставки
Distributed systems 101: сообщение может потеряться, дублироваться или прийти не в том порядке - это факт жизни, а не баг конкретного брокера. Три уровня гарантий отличаются не надёжностью, а ценой: at-most-once быстро и может терять, at-least-once надёжно но может дублировать, exactly-once медленно и дорого. Stripe в 2023 году публично признал, что их payment webhook система работает на at-least-once с idempotency keys на стороне получателя - именно потому что exactly-once на уровне брокера prohibitively expensive.
**At-most-once**: producer отправил и забыл; consumer не подтверждает; сообщение может потеряться - но никогда не дублируется. Для метрик, логов - нормально. **At-least-once**: producer повторяет при таймауте; consumer подтверждает после обработки; сообщение точно придёт, но возможны дубликаты. Требует идемпотентной обработки. **Exactly-once**: 2-phase commit или transactional outbox; максимальные гарантии, максимальная сложность и latency. Kafka Transactions предоставляют exactly-once внутри Kafka-кластера.
Система списания денег с карты случайно обработала одно сообщение дважды. Какая гарантия доставки использовалась и чего не хватало?
Backpressure
Backpressure - это механизм, при котором медленный consumer сигнализирует быстрому producer замедлиться. Без backpressure разница в скорости накапливается в памяти брокера: в 2019 году один из крупных европейских банков потерял 4 часа продуктивности когда очередь транзакций переполнилась и брокер начал OOM-killing процессы. Правильный backpressure - это не катастрофа, это нормальный способ саморегуляции системы под нагрузкой.
Механизмы backpressure: **Consumer-driven** - consumer явно указывает prefetch count (RabbitMQ basic.qos): 'не давай мне больше N сообщений, пока не подтвержу'. **Queue depth monitoring** - при росте очереди выше порога увеличивают количество consumer'ов (autoscaling) или замедляют producer. **Rate limiting на producer** - producer проверяет глубину очереди перед публикацией. В Kafka backpressure работает иначе: producer сам контролирует lag через Consumer Group lag metrics.
Backpressure - это проблема, которую нужно устранить; правильная система не должна испытывать backpressure
Backpressure - это нормальный сигнал саморегуляции системы; правильный дизайн не устраняет backpressure, а реагирует на него: масштабирует consumer'ы, замедляет producer или отказывает с явной ошибкой
Попытка 'устранить' backpressure путём безлимитного буферирования приводит к OOM и неконтролируемой деградации. Явный backpressure + ограниченные буферы - это controlled degradation, а не сбой.
RabbitMQ consumer установил prefetch=1. Что это означает для производительности?
Ключевые идеи
- **Брокер разрывает синхронный каскад**: producer публикует и не ждёт consumer - temporal, rate и space decoupling одновременно; отказ одного компонента не каскадирует в остальные
- **Очередь vs Топик**: очередь - для работы (один worker берёт задачу), топик - для событий (все подписчики получают копию); выбор определяется бизнес-требованием, не предпочтением технологии
- **At-least-once + идемпотентность** - золотой стандарт: exactly-once слишком дорог, at-most-once слишком ненадёжен; большинство production систем используют at-least-once с idempotency key
Связанные темы
Брокеры сообщений - основа event-driven архитектуры:
- RabbitMQ: AMQP, exchanges, очереди — Конкретная реализация брокера с поддержкой очередей и топиков через exchange routing
- WebSocket и Server-Sent Events — Часто брокер стоит за WebSocket gateway: broker -> gateway -> client в реальном времени
Вопросы для размышления
- Если at-least-once с idempotency дешевле exactly-once, то какие случаи всё же требуют exactly-once - и как понять, что текущая система недостаточно надёжна?
- Backpressure замедляет producer при перегрузке consumer. Но что делать, если producer нельзя замедлить (биржевой поток данных, GPS-трекинг)? Какие альтернативы?
- Компания переходит с синхронного REST на async брокер. Как изменится UX: что пользователь видит, пока задача в очереди, и как коммуницировать ошибки, возникающие async?