Асинхронное взаимодействие разрывает отправителя и получателя во времени: producer публикует сообщение и не ждёт немедленного ответа. Для системного аналитика это ключевой способ описать интеграцию между сервисами, когда синхронный вызов (03.05.02 Синхронные протоколы (REST, gRPC, GraphQL)) создаёт жёсткую связность или не выдерживает пиковые нагрузки. В этой статье — модели доставки, гарантии как требования, выбор между очередью и логом, спецификация Producer/Consumer и граница с событийной архитектурой.
1. Суть темы
В синхронной модели клиент отправляет запрос и ожидает ответ. Это просто, но создаёт временную связку: недоступность сервера — отказ клиенту, длительные операции держат соединение, адрес получателя должен быть известен заранее.
Асинхронный обмен через брокер сообщений (message broker) решает часть этих проблем:
- Временная развязка (temporal decoupling) — отправитель может послать сообщение, когда получатель не в сети; получатель обработает его позже.
- Пространственная развязка (spatial decoupling) — отправителю не нужен сетевой адрес получателя; достаточно имени очереди или топика в рамках согласованной конвенции (кластер, vhost, окружение, ACL — см. 03.05.05 Безопасность API).
- Буферизация пиков — брокер накапливает сообщения, сглаживая нагрузку.
- Настраиваемая надёжность доставки — брокер может хранить сообщение и повторять попытки, но конкретная семантика (at-most-once, at-least-once, exactly-once) задаётся конфигурацией и контрактом, а не самим фактом использования очереди.
- Горизонтальное масштабирование — несколько экземпляров consumer'ов параллельно обрабатывают поток (competing consumers).
Компромиссы тоже явные: дополнительная инфраструктура, выше задержка (latency), сложнее сквозная observability (нужны traceId / correlationId — см. раздел 3), сложнее согласованность данных без явных требований (Outbox, Saga — см. 03.05.06 Event-Driven Architecture, 03.06.01 Архитектурные паттерны распределённых систем).
Асинхронность — часть интеграционного ландшафта наряду с API-led и шинами (03.05.01 Виды интеграции (точка-точка, ESB, API-led)). Аналитик взвешивает компромиссы через нефункциональные требования к производительности, надёжности и связанности.
Когда async, когда sync
| Бизнес-ситуация | Предпочтительный подход |
|---|---|
| Быстрая операция, нужен немедленный ответ | Синхронный API (03.05.02 Синхронные протоколы (REST, gRPC, GraphQL)) |
| Длительная обработка (> нескольких секунд) | Очередь задач |
| Несколько сервисов независимо реагируют на один факт | Pub/sub, событийный топик |
| Нужна история событий, повторное чтение, потоковая аналитика | Log/stream (Kafka) |
| Нужен ответ, но без удержания HTTP-соединения | Request–Reply over messaging (временная reply-очередь + correlationId) |
| Критична строгая консистентность «здесь и сейчас» без компенсаций | Синхронный вызов |
| Нужна согласованность через несколько async-шагов с компенсациями | Saga — явно асинхронная модель, не замена sync «в лоб» |
Комбинированный паттерн API + domain events часто работает так: REST принимает команду и возвращает идентификатор; результат и побочные эффекты рассылаются асинхронно. Webhook/callback для уведомления партнёра — по сути синхронный HTTP-вызов «в обратную сторону» (07.02 Работа с API (практика)), а не брокер сообщений.
Request–Reply over messaging — отдельный случай: клиент кладёт запрос в очередь с correlationId и replyTo; обработчик отвечает в указанную reply-очередь с тем же ID. Это async по транспорту, но контракт предполагает ответ — фиксируйте таймаут ожидания, формат ответа и семантику at-least-once (дубликаты ответа).
2. Ключевые понятия
Участники и артефакты
- Сообщение (Message) — единица данных: заголовки (метаданные) и тело (полезная нагрузка: JSON, Avro, Protobuf). Формат обёртки (CloudEvents и др.) — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf).
- Брокер сообщений (Message Broker) — промежуточное ПО, принимающее, сохраняющее и доставляющее сообщения (RabbitMQ, Apache Kafka, AWS SQS и др.).
- Producer (издатель) — приложение, публикующее сообщения.
- Consumer (потребитель) — приложение, получающее и обрабатывающее сообщения.
- Consumer Group (Kafka) — группа consumer'ов, совместно читающих топик; каждое сообщение в группе обрабатывается одним consumer'ом.
- Poison message (poison pill) — сообщение, которое consumer не может обработать даже после всех retry (битая схема, невалидные данные, постоянная ошибка бизнес-логики); после исчерпания попыток уходит в DLQ.
Очередь vs топик vs log — не путать модели
| Модель | Семантика | Кто получает сообщение | После чтения |
|---|---|---|---|
| Очередь (queue) — point-to-point | Задача на обработку | Один consumer из группы (competing consumers) | Сообщение удаляется (RabbitMQ ack, SQS delete) |
| Pub/sub (топик) | Широковещательное событие | Каждый подписчик / consumer group | Зависит от брокера |
| Log/stream (Kafka) | Append-only журнал | Каждая consumer group читает независимо | Сообщение остаётся до истечения retention |
RabbitMQ — классические очереди и гибкая маршрутизация (direct, topic, fanout). Fanout доставляет всем подписчикам; очередь с competing consumers — только одному worker'у. Kafka — распределённый log: сообщения не удаляются после чтения, позиция фиксируется offset'ом; «все сервисы видят событие» достигается разными consumer group, а «ровно один обработчик» — одной group с competing consumers.
Модели доставки
Point-to-Point (очередь): producer → очередь → один consumer забирает и подтверждает (ack). Сообщение исчезает.
sequenceDiagram
participant P as Producer
participant B as Broker (Queue)
participant C1 as Consumer 1
participant C2 as Consumer 2
P->>B: Отправить сообщение
B-->>P: Publisher confirm
C1->>B: Получить сообщение
B->>C1: Сообщение
C1->>C1: Обработка
C1->>B: Ack
Note over B: Сообщение удалено
Publish-Subscribe: producer публикует в топик; каждая consumer group (Kafka) или каждый подписчик (RabbitMQ fanout) получает копию.
sequenceDiagram
participant P as Producer
participant B as Broker (Topic)
participant C1 as Consumer Group A
participant C2 as Consumer Group B
P->>B: Опубликовать событие
B->>C1: Доставить
B->>C2: Доставить (независимо)
C1-->>B: Ack / commit offset
C2-->>B: Ack / commit offset
Команда vs событие
Центральное различение для именования и контрактов. Подробнее о паттернах EDA — 03.05.06 Event-Driven Architecture; о domain events — 03.06.02 Основы Domain-Driven Design.
| Команда (Command) | Событие (Event) | |
|---|---|---|
| Смысл | Просьба выполнить действие | Факт, который уже произошёл |
| Именование | CreateOrder, ProcessPayment | OrderCreated, PaymentReceived |
| Время | Imperative | Past tense |
| Получатели | Обычно один исполнитель | Ноль или много подписчиков |
| Идемпотентность | Обязательна при at-least-once | Обязательна при at-least-once |
| Типичный канал | Очередь задач | Топик / log |
Domain event vs integration event
Domain event — факт внутри bounded context; владелец схемы — команда домена. Integration event — сообщение на границе сервисов: что именно уходит в топик/очередь, в какой схеме, кто владелец контракта.
В спецификации интеграционного контракта аналитик фиксирует:
- имя топика/очереди и
eventType(integration event, не внутренний domain event); - минимальный payload для потребителей (часто подмножество domain-модели);
- владельца схемы и правило эволюции (03.05.07 Управление версионированием API и жизненным циклом);
- mapping: какой domain event порождает какой integration event и при каком бизнес-триггере.
Детали DDD — 03.06.02 Основы Domain-Driven Design; паттерны публикации — 03.05.06 Event-Driven Architecture.
Основные технологии (обзор для выбора модели)
RabbitMQ (AMQP): exchange → binding → queue; publisher confirms, manual ack/nack; типично — фоновые задачи, отложенная обработка.
Apache Kafka: топики с партициями, append-only log; порядок только внутри партиции; retention и compaction — отдельные NFR (важно для PII/GDPR — см. 08.02 Нефункциональные требования (НФТ) — классификация и стандарты); сценарии — event streaming, несколько независимых потребителей, повторное чтение.
Управляемые очереди (AWS SQS/SNS, Google Pub/Sub, Azure Service Bus): SQS — point-to-point, at-least-once; consumer удаляет сообщение (DeleteMessage) после успеха; при сбое до delete — повторная доставка после visibility timeout. FIFO-очередь гарантирует порядок в рамках message group (аналог partition key в Kafka).
3. Как применяется в работе аналитика
3.1. Гарантии доставки — формулировать как требования
Асинхронность не даёт «гарантированную доставку» по умолчанию. Семантику нужно явно указать в спецификации:
| Семантика | Поведение | Когда выбирать |
|---|---|---|
| At-most-once | Может потеряться, не дублируется | Метрики, логи, некритичные уведомления |
| At-least-once | Доставится, возможны дубликаты | Большинство бизнес-потоков; consumer обязан быть идемпотентным |
| Exactly-once processing | Ровно одна обработка сообщения на стороне consumer | Деньги, инвентарь; дедуп по messageId, идемпотентный consumer, транзакции Kafka, idempotent producer |
Exactly-once processing — про семантику доставки и обработки: как гарантировать, что одно и то же сообщение не применится дважды (хранилище обработанных ID, transactional consume–produce в Kafka). Это не то же самое, что Saga или Outbox.
Бизнес-инварианты «без двойного списания» через несколько систем — отдельный слой требований:
| Проблема | Паттерн в требованиях |
|---|---|
| Запись в БД и публикация в брокер должны быть атомарны | Transactional Outbox |
| Цепочка async-шагов без единой транзакции, но с компенсациями при сбое | Saga (choreography или orchestration) |
| Финансы, резерв запасов, любой сценарий «двойного списания» недопустим | Outbox + идемпотентность + при необходимости Saga |
Outbox и Saga решают распределённую согласованность процесса, а не заменяют формулировку exactly-once processing в спецификации consumer'а. Детали — 03.05.06 Event-Driven Architecture.
Для at-least-once зафиксируйте в критериях приёмки consumer'а: повторная обработка того же messageId не создаёт дублирующих побочных эффектов.
3.2. Порядок сообщений (ordering)
| Вопрос в спецификации | Решение |
|---|---|
| Нужен строгий порядок для одной сущности? | Partition key = orderId (Kafka) или FIFO queue / message group (SQS) |
| Можно обрабатывать параллельно? | Несколько партиций / competing consumers без общего ключа |
| Порядок между разными сущностями? | Обычно не требуется |
| Глобальный порядок по всему топику? | Не гарантируется — только внутри ключа / партиции / message group |
Если порядок критичен — явно запретите параллельную обработку сообщений одного ключа.
3.3. Контракт async-интерфейса: AsyncAPI и схемы
Специфицируйте асинхронные интерфейсы не менее детально, чем REST. Формат контракта — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf): AsyncAPI для Kafka/RabbitMQ наравне с OpenAPI для REST; единый envelope (CloudEvents) — там же. Схема сообщения — JSON Schema или Avro; правила эволюции — 03.05.07 Управление версионированием API и жизненным циклом.
Готовые шаблоны в базе (10.04 Чеклист аналитика перед стартом разработки — проверка полноты перед стартом):
- 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ)
- 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)
Producer — обязательные поля спецификации:
- Бизнес-триггер публикации
- Топик/очередь (имя, тип, окружение)
- Схема сообщения и версия (
schemaVersion) - Ключ партиционирования / routing key
- Заголовки:
traceId,correlationId,eventType,messageId - Семантика доставки и идемпотентность producer'а (не только consumer'а)
- Обработка ошибок отправки (retry, circuit breaker)
Consumer — обязательные поля:
- Источник, consumer group / имя очереди
- Логика обработки (шаги)
- Идемпотентность: ключ дедупликации, хранилище обработанных ID
- Retry: max attempts, backoff, poison message → DLQ
- DLQ: владелец очереди, runbook, SLA ручной обработки, алертинг
- Подтверждение: Kafka — commit offset после успеха; RabbitMQ — ack/nack; SQS —
DeleteMessageпосле успеха, visibility timeout при длительной обработке - Пропускная способность, допустимый consumer lag
NFR-чеклист для async (дополняет NFR или companion к AsyncAPI):
- Latency end-to-end, throughput (msg/sec)
- Retention, max message size
- Consumer lag (порог алерта)
- RPO/RTO для брокера
- Обязательность
traceId/correlationIdдля distributed tracing и сквозной диагностики (08.02 Нефункциональные требования (НФТ) — классификация и стандарты; инфраструктура — 07.06 CICD и конвейеры — влияние на требования) - Auth/TLS для брокера (03.05.05 Безопасность API)
Вопросы на elicitation (владельцы и процессы):
- Кто утверждает breaking change схемы integration event и срок миграции потребителей?
- Кто владелец DLQ и runbook при poison message?
- Кто отвечает за retention и удаление PII в брокере (08.05 Управление рисками на этапе анализа)?
3.4. Паттерны интеграции (EIP)
Из Enterprise Integration Patterns (Hohpe, Woolf). Укажите в требованиях где фиксируется каждый паттерн:
| Паттерн | Где в артефактах |
|---|---|
| Competing Consumers | Consumer spec, NFR (throughput) |
| Dead Letter Queue (DLQ) | Consumer spec + runbook (владелец, SLA) |
| Retry with Backoff | Consumer spec (max attempts, backoff) |
| Correlation Identifier | Producer/Consumer spec (заголовки) |
| Message Expiry (TTL) | Producer spec или NFR (retention) |
3.5. Пример спецификации потока
Producer: OrderCreated
- Топик:
shop.order.events(Kafka) / очередьshop.order.created(RabbitMQ)- AsyncAPI: см. репозиторий контрактов, версия 1.2.0
- Схема:
{"orderId": "uuid", "customerId": "uuid", "totalAmount": decimal, "createdAt": "ISO8601"}- Ключ партиции / routing key:
orderId- Семантика: at-least-once; producer — idempotent; consumer проверяет
orderIdв таблице обработанных
Вариант A — Kafka (commit offset после успеха):
sequenceDiagram
participant OrderSvc as Order Service
participant Broker as Kafka
participant Billing as Billing Service
participant DLQ as Dead Letter Topic
OrderSvc->>OrderSvc: Создать заказ (БД)
OrderSvc->>Broker: OrderCreated
Broker-->>OrderSvc: Ack (producer confirm)
Broker->>Billing: Доставка
Billing->>Billing: Дедупликация, оплата
alt Успех
Billing->>Broker: Commit offset
else Ошибка после retry
Billing->>DLQ: Poison message
end
Вариант B — RabbitMQ (manual ack после успеха):
sequenceDiagram
participant OrderSvc as Order Service
participant Broker as RabbitMQ
participant Billing as Billing Service
participant DLQ as Dead Letter Queue
OrderSvc->>OrderSvc: Создать заказ (БД)
OrderSvc->>Broker: OrderCreated
Broker-->>OrderSvc: Publisher confirm
Broker->>Billing: Доставка
Billing->>Billing: Дедупликация, оплата
alt Успех
Billing->>Broker: Ack
else Ошибка после retry
Billing->>Broker: Nack → DLQ
end
Моделируйте потоки через 03.02.03 Диаграмма последовательности (Sequence Diagram); параллельные ветки и таймеры — 03.02.02 Диаграмма деятельности (Activity Diagram).
3.6. Матрица выбора модели
| Бизнес-вопрос | Очередь (task) | Log/stream (Kafka) | Pub/sub (fanout / несколько groups) |
|---|---|---|---|
| Обработать задачу ровно одним worker'ом | ✓ (competing consumers) | ✓ (одна consumer group) | ✗ — доставка всем подписчикам; для «одного worker'а» нужна очередь или одна group, не fanout |
| Все сервисы видят одно событие | ✗ | ✓ (разные consumer groups) | ✓ (fanout / отдельные подписки) |
| Перечитать историю / replay | ✗ | ✓ | ✗ |
| Простота, классические фоновые job'ы | ✓ (RabbitMQ, SQS) | избыточно | — |
| Строгий порядок по ключу | FIFO SQS / одна очередь | partition key (порядок внутри партиции) | зависит от брокера |
3.7. Верификация и приёмка async-интеграции
Асинхронный контракт проверяется не только «на staging вручную». Зафиксируйте в V&V и тестировании требований:
- Контрактные тесты producer/consumer по AsyncAPI и схеме (совместимость версий).
- Тестовый топик/очередь — изоляция от prod, воспроизводимые сценарии.
- Симуляция duplicate delivery — повторная обработка того же
messageIdне ломает инварианты. - Сценарий «consumer отстаёт» — consumer lag выше порога, алерт, поведение при backlog.
- Poison message — сообщение уходит в DLQ по критериям из spec; владелец обрабатывает по runbook.
Критерии приёмки — 02.04.02 Критерии приёмки (Acceptance Criteria).
4. Типовые ошибки и ограничения
- «Очередь = гарантированная доставка» — без явной семантики и ack/nack или publisher confirms доставка не гарантирована.
- Путаница RabbitMQ queue и Kafka topic — «один раз обработать» vs «все подписчики видят»; проверяйте модель до выбора технологии.
- Смешение fanout и competing consumers — pub/sub доставляет всем; «ровно один worker» — очередь или одна Kafka consumer group.
- Exactly-once = Saga — Saga — про многошаговый процесс с компенсациями; exactly-once processing — про дедупликацию обработки одного сообщения.
- Commit offset / ack / DeleteMessage до завершения обработки — потеря сообщения при сбое; фиксируйте порядок в спецификации consumer'а.
- Идемпотентность только на consumer'е — при at-least-once нужен и идемпотентный producer (дубли при retry отправки).
- Событие названо как команда —
CreateOrderв топике с несколькими подписчиками создаёт неоднозначность; используйте past tense для фактов. - Domain event напрямую в топик — без явного integration event и владельца схемы ломается эволюция контракта.
- Раздувание § EDA в интеграционной статье — Event Sourcing, CQRS детально живут в 03.05.06 Event-Driven Architecture, не дублируйте.
- Пример только под Kafka — для RabbitMQ и SQS другая модель подтверждений; указывайте брокер проекта.
- DLQ без процесса — очередь-помойка без владельца, SLA и алертов бесполезна.
- Retention и PII — в Kafka сообщения хранятся; для персональных данных задайте retention, шифрование, политику удаления (08.05 Управление рисками на этапе анализа).
- Webhook вместо очереди у партнёра — callback — синхронный HTTP; для надёжности нужны retry, idempotency key на стороне получателя; при нестабильном партнёре — очередь между вашей системой и исходящим webhook'ом, чтобы не терять события при его недоступности.
- Fire-and-forget vs request–reply — если бизнес ждёт ответ, но вы описали только publish без reply-очереди и
correlationId, контракт неполон.
Связи с другими разделами базы
- 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf) — AsyncAPI, CloudEvents, Avro, Protobuf как контракт async-интерфейса.
- 03.05.02 Синхронные протоколы (REST, gRPC, GraphQL) — синхронный контрагент, комбинация API + events.
- 03.05.06 Event-Driven Architecture — паттерны EDA, Saga, Outbox, Event Sourcing, CQRS.
- 03.05.01 Виды интеграции (точка-точка, ESB, API-led) — место async в интеграционном ландшафте.
- 03.05.07 Управление версионированием API и жизненным циклом — эволюция схем событий.
- 03.05.05 Безопасность API — ACL, TLS, аутентификация брокера.
- 03.06.01 Архитектурные паттерны распределённых систем — компромиссы консистентности и latency.
- 03.06.02 Основы Domain-Driven Design — domain events vs integration events.
- 03.01.04 Интерфейсы и интеграционные взаимодействия — общие принципы проектирования интерфейсов.
- 03.02.03 Диаграмма последовательности (Sequence Diagram) — моделирование потоков сообщений.
- 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ) и 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ) — шаблоны спецификаций.
- 10.04 Чеклист аналитика перед стартом разработки — проверка полноты async-контракта перед стартом.
- 02.03.01 Классификация требований (БТ, ФТ, НФТ) — НФТ по надёжности и идемпотентности.
- 02.04.01 Верификация и валидация (V&V) — приёмка async через контрактные тесты и сценарии сбоя.
- 02.04.02 Критерии приёмки (Acceptance Criteria) — приёмка consumer'а и DLQ.
- 08.02 Нефункциональные требования (НФТ) — классификация и стандарты — latency, retention, throughput, observability.
- 08.03 Тестирование требований (статическое тестирование) — тестовые сценарии duplicate delivery и consumer lag.
- 08.05 Управление рисками на этапе анализа — риски потери сообщений, DLQ, retention/PII.
Источники и дополнительные материалы
Книги и стандарты
- Hohpe, G., Woolf, B. (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley.
- Kleppmann, M. (2017). Designing Data-Intensive Applications. O'Reilly. — Главы о гарантиях доставки и потоковой обработке.
- Newman, S. (2015). Building Microservices. O'Reilly. — Асинхронная интеграция в микросервисах.
- Richardson, C. (2019). Microservices Patterns. Manning. — Saga, Outbox, Event Sourcing, CQRS.
Спецификации и документация
- AsyncAPI Specification. https://www.asyncapi.com/docs
- Apache Kafka Documentation. https://kafka.apache.org/documentation/
- Kafka — Message Delivery Semantics. https://kafka.apache.org/documentation/#semantics
- RabbitMQ Tutorials. https://www.rabbitmq.com/getstarted.html
- RabbitMQ — Reliability Guide. https://www.rabbitmq.com/docs/reliability
- Enterprise Integration Patterns (сайт). https://www.enterpriseintegrationpatterns.com/
Дополнительно
- Confluent — Kafka Introduction. https://docs.confluent.io/kafka/introduction.html