Consumer — компонент, который читает сообщения из брокера (топик Kafka, очередь RabbitMQ), десериализует их, выполняет бизнес-логику и фиксирует прогресс обработки (commit offset / ack). Шаблон фиксирует алгоритм обработки, политику commit/ack, идемпотентность и реакцию на ошибки — для передачи в разработку и QA. Теория брокеров и EDA — 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events) и 03.05.06 Event-Driven Architecture; контракт сообщения — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf) и парный 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ).

1. Назначение

Спецификацию Consumer пишет системный аналитик; её читают разработка, QA, платформенная команда и владельцы Producer.

Шаблон закрывает пять задач:

  • Подключение и конфигурация — group/queue, prefetch, offset reset, параллелизм; отдельные поля для Kafka и RabbitMQ.
  • Политика фиксации прогресса — когда commit/ack относительно побочных эффектов (БД, API, события); единое правило для разработки и приёмки.
  • Алгоритм обработки — шаги после валидации, идемпотентность, порядок сообщений.
  • Ошибки и DLT/DLQ — retry, poison pill, dead letter; обязательное поведение по offset/ack при отправке в DLT.
  • Эксплуатация — метрики, SLI/SLO, критерии приёмки, трассировка к FR/NFR (10.01.15 Шаблон матрицы трассировки, 02.04.02 Критерии приёмки (Acceptance Criteria)).

Граница артефакта: шаблон описывает потребителя сообщений. Не дублирует учебник по Kafka/RabbitMQ — для моделей доставки и паттернов см. 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events). Исходящие синхронные вызовы (REST) — 10.01.04 Шаблон спецификации REST API; схемы таблиц — 10.01.03 Шаблон спецификации БД сервиса.

Один документ — один брокер и один вход. Kafka и RabbitMQ различаются семантикой ack, DLX и контрактом подписки. Если prod — Kafka, а staging — RabbitMQ «зеркало», оформите отдельную спеку на staging или явный под-scope в «Scope компонентов» с отдельными §3.2–3.3 для каждого брокера. Несколько listener'ов одного брокера на разные топики/очереди — один документ с перечнем ID в §3.0.

2. Когда использовать

Подходит, если:

  • сервис подписан на топик Kafka или очередь RabbitMQ и выполняет бизнес-логику по событию;
  • нужен человекочитаемый документ с алгоритмом, матрицей ошибок, идемпотентностью и NFR;
  • есть или планируется спека Producer — consumer должен ссылаться на тот же контракт.

Не подходит или требует дополнения, если:

Входные условия: утверждённый контракт Producer или AsyncAPI; границы сервиса (03.01.04 Интерфейсы и интеграционные взаимодействия); термины payload (10.01.14 Шаблон глоссария и словаря данных).

3. Структура документа

Скопируйте блок в Confluence или Git. Поля обяз. нужны для утверждения; да* — при выполнении условия в скобках.

3.0. Шапка governance

ПолеОбяз.Что писать
Scope компонентовдаID consumer(ов); один тип брокера на документ (или явный под-scope на каждый брокер); несколько listener'ов — перечень топиков/очередей
Версия документадаv1.0
СтатусдаDraft → Review → Approved
Источник истины контрактада«AsyncAPI orders-events v1.2 / Producer spec 10.01.06-OrderEvents / Schema Registry subject orders.events-value» — одно правило приоритета
AsyncAPI channel / operation idда**При наличии AsyncAPI — channels.orders.events, operationId: onOrderCreated; bindings (Kafka group, RabbitMQ queue), reply-to при request-reply — отделяет контракт подписки от схемы payload (03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf))
Связанные требованиядаFR-…, NFR-…; матрица — 10.01.15 Шаблон матрицы трассировки

3.1. Общая информация

ПолеОбяз.Что писать
Наименование ConsumerдаУникальное имя, напр. OrderCreatedConsumer
Бизнес-контекстдаНа какое событие реагирует, зачем, ссылка на UC/BPMN
Владелец компонентадаКоманда/сервис
Тип брокерадаApache Kafka или RabbitMQ — один на документ
Входной топик / exchange + очередьдаKafka: orders.events; RabbitMQ: exchange orders.topic, queue orders.notifications, binding key order.created.v1
Ожидаемый форматдаJSON / Avro / Protobuf; ссылка на схему и версию — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf)
Subject / schema versionда**Avro/Protobuf — subject Schema Registry, compatibility level; JSON — $id схемы в Git
Гарантия доставки (целевая)даЗафиксировать один режим (см. таблицу ниже)
РежимКогда выбиратьЧто меняется в спеке
At-least-once (по умолчанию)Большинство доменных consumer; допустимы повторыОбязательна идемпотентность §3.7; commit/ack после побочных эффектов §3.3
Exactly-once (Kafka transactions)End-to-end EOS с transactional producer + read-process-write в одной транзакции KafkaОбязателен ADR (10.01.13 Шаблон архитектурного решения (ADR)); см. блок EOS ниже
At-most-onceTelemetry, некритичные метрикиCommit/ack до обработки; потери допустимы — явное ADR

Для RabbitMQ «exactly-once» на уровне брокера не гарантируется — используйте at-least-once + идемпотентность или outbox/inbox на стороне приложения.

Exactly-once (Kafka) — минимальный набор полей в спеке:

ПолеЧто писать
ADRСсылка на 10.01.13 Шаблон архитектурного решения (ADR) с обоснованием EOS vs at-least-once + inbox
isolation.levelread_committed
transactional.idУникальный id consumer/producer-пары в транзакции
Связка с outboxКак transactional producer и consumer делят границу транзакции с БД
Критерий «обработано»Что считается commit offset: после send offsets в транзакции, не только запись в eventId
read-process-writeКакие топики входят в одну транзакцию; idempotence producer на исходящих
ПолеKafkaRabbitMQ
Требование к порядкуПорядок только внутри партиции; указать, нужен ли порядок по ключуПорядок в одной очереди при одном consumer; при нескольких — FIFO не гарантирован
Replay / backfill — технический способНовая consumer group + auto.offset.reset=earliest; reset offsets по runbookRequeue / republish из DLQ — отдельный runbook
Replay / backfill — governanceда**Обязательно при replay: инициатор (роль/команда), окно (from/to offset или timestamp), влияние на идемпотентность (записи PROCESSED / TTL §3.7), согласование с 03.05.07 Управление версионированием API и жизненным циклом

3.2. Параметры подключения и конфигурация

ПараметрОбяз.Что писать
Брокер / кластердаАдрес и порт; prod vs staging
Имя Consumer Group / queueдаKafka: order-service-notification-group; RabbitMQ: имя очереди
Автосброс offset / начало чтениядаKafka: auto.offset.reset = earliest / latest; RabbitMQ: новая очередь vs существующая
Максимальный размер батчадаKafka: max.poll.records; RabbitMQ: prefetch (см. ниже)
Таймаут опроса / обработкидаKafka: max.poll.interval.ms; RabbitMQ: consumer timeout
ПараллелизмдаЧисло потоков / listener containers; ≤ числа партиций (Kafka)
Graceful shutdownдаIn-flight: дождаться завершения или таймаут; не commit/ack незавершённых

Kafka (дополнительные поля):

ПараметрОбяз.*Что писать
Подписка: fixed topic vs patternда*Fixed: orders.events; pattern: orders.* — влияет на rebalance при появлении топиков
Compacted topic / tombstonesда**Для compacted — поведение при null value (delete): пропуск / soft-delete в БД
Стратегия assignorнетRange / CooperativeSticky
enable.auto.commitда*Обычно false — ручной commit по §3.3
isolation.levelда**При EOS — read_committed
max.partition.fetch.bytesнетЛимит fetch на партицию; согласовать с max размером одного сообщения (§3.13) — fetch может быть больше одного record

RabbitMQ (дополнительные поля):

ПараметрОбяз.*Что писать
Exchange и bindingда*Exchange, type, routing key / binding pattern
Ack modeда*manual (рекомендуется) / auto; при manual — когда ack/nack (§3.3)
Prefetch countда*напр. 10 — лимит unacked сообщений на канал
Requeue при nackда*requeue=false для poison; true только для transient
DLX (Dead Letter Exchange)да*Exchange + routing key для отбраковки; max-length / TTL очереди при необходимости
Quorum / classic queueнетТип очереди и политика HA

Безопасность канала (чеклист, детали — 03.05.05 Безопасность API):

ПараметрЧто писать
TLSОбязателен на prod
АутентификацияSASL (Kafka), username/password или cert (RabbitMQ)
ACLRead/consume на топик или очередь

3.3. Политика commit / ack (критичное поле)

Зафиксируйте одно правило для всего consumer. Без этого разработка и QA расходятся в трактовке повторов.

РешениеОписаниеКогда
Commit/ack после всех побочных эффектов (рекомендуется при at-least-once)Offset/ack только после успешной записи в БД, вызовов API и смены статуса идемпотентности на PROCESSEDДопустимы повторы при сбое до commit; идемпотентность обязательна
Commit/ack до побочных эффектовРедко; риск потери при падении после ackТолько at-most-once или асинхронная компенсация — явный ADR (10.01.13 Шаблон архитектурного решения (ADR))

Нормативная формулировка для шаблона (at-least-once):

  1. Десериализация и валидация.
  2. Claim идемпотентности (§3.7) — запись PROCESSING или эквивалент.
  3. Побочные эффекты (БД, API, publish).
  4. Статус PROCESSED.
  5. Только then — commit offset (Kafka) или ack (RabbitMQ manual).

При падении между шагами 3–4: сообщение будет доставлено повторно; consumer должен безопасно продолжить (идемпотентность §3.7).

Батч (Kafka): commit — последний непрерывный offset по каждой партиции (last contiguous committed offset per partition), а не произвольный набор «успешных из батча». При таймауте max.poll.interval — не commit незавершённых; сообщения перечитаются.

Батч (RabbitMQ): ack по одному сообщению или batch ack только для полностью обработанных delivery tag; nack с requeue=false при отправке в DLX.

DLT / DLQ — обязательное поведение:

БрокерДействиеCommit / ack
KafkaPublish в DLT-топик с исходным payload и headers ошибкиCommit offset исходного сообщения после успешной записи в DLT (иначе бесконечный цикл)
RabbitMQPublish в DLX / DLQ или nack → DLXAck исходного сообщения после успешной маршрутизации в DLQ (или reject без requeue)

3.4. Десериализация и валидация

АспектЧто писать
СхемаСсылка на JSON Schema / Avro / Protobuf; subject и версия — как в Producer
Schema RegistryURL, стратегия проверки (FAIL vs WARN)
ПроверкиОбязательные поля, бизнес-правила (напр. order.totalAmount > 0)
Невалидное сообщение (poison pill)Без retry → DLT/DLQ + метрика; см. §3.3 для commit/ack
Тип ошибкиПоведениеRetry
Невозможно десериализоватьDLT/DLQ, poison_message_total++0
Бизнес-валидация не пройденаDLT/DLQ с заголовком причины0
Временный сбой инфраструктурыRetry по §3.6да

3.5. Алгоритм обработки

Опишите нумерованный список — он должен совпадать с диаграммой последовательности в §4. Шаги 4–5 и побочные эффекты §3.8 — каркас: состав эффектов определяете по фиче, не копируйте email из примера без необходимости.

ШагДействие
1Десериализовать в доменный объект
2SELECT по ключу идемпотентности — если PROCESSED, commit/ack и выход
3Claim: INSERT PROCESSING (§3.7). При конфликте (запись уже есть) — повторный SELECT и ветвление:
3aPROCESSED → commit/ack, выход
3bPROCESSING (другой worker) → bounded wait (таймаут T, напр. 30 s) с повторным SELECT; по истечении T без PROCESSED — алерт + не ack (redelivery) или skip + ack только если зафиксирован барьер «эффект уже выполнен» (§3.7) — одна ветка в спеке
3cFAILED → см. §3.7 (reprocess / DLT)
3dclaim успешен → шаг 4
4Побочные эффекты (§3.8): БД, REST (10.01.04 Шаблон спецификации REST API), опционально publish
5UPDATE PROCESSED; при исчерпании retry без успеха — UPDATE FAILED + DLT/DLQ (§3.6)
6Commit offset / ack только после шага 5 (PROCESSED) или после DLT; при FAILED в DLT — commit/ack по §3.3

Порядок сообщений: если критичен — зафиксируйте ключ партиции (Kafka) или single active consumer (RabbitMQ); иначе — «порядок не требуется».

Rebalance / деплой: при graceful shutdown — завершить in-flight до max.poll.interval; не commit частично обработанных. При rebalance mid-flight — in-flight завершается или откатывается без commit; сообщение будет доставлено повторно.

3.6. Обработка ошибок

Таблица ниже — образец для домена «уведомление по заказу»; строки замените на эффекты из §3.8. Retry: N попыток, политика backoff и jitter — согласовать с платформой (шаблон: exponential backoff, не фиксировать ms as-built).

СценарийОбработкаRetryDLT/DLQCommit/ack
Временная недоступность БДBackoff, повтор шагов 4–5N×, backoff — платформапосле исчерпания → FAILED + DLTтолько после PROCESSED или DLT
Transient сбой внешнего API (5xx)Retry; идempotency key или PROCESSING как барьерN×после исчерпаниясм. §3.3
Permanent ошибка API (4xx)DLT, алерт0дапосле DLT
Дубликат (PROCESSED)Пропуск––commit/ack
Poison (§3.4)DLT/DLQ0дапосле DLT
Исчерпание retry, эффект не выполненUPDATE FAILED, DLT/DLQ0дапосле DLT
FAILED + ручной reprocessOps/поддержка: сброс FAILED → PROCESSING или новая delivery после fixпо runbookопциональноcommit/ack только после PROCESSED или DLT
Превышение max.poll.intervalНе commit; сообщения перечитаютсяimplicit––

DLT/DLQ: имя топика/очереди; сохранять исходное тело, headers, timestamp, errorCode.

Retry vs частично выполненный эффект: если внешний сервис не идемпотентен — зафиксируйте барьер: «повтор вызова только до первого успешного ответа; при redelivery при PROCESSING — не вызывать повторно, завершить UPDATE PROCESSED». Статус PROCESSING — барьер от двойного эффекта.

3.7. Идемпотентность и дедупликация

ПолеЧто писать
Ключ идемпотентностиeventId из сообщения (согласован с Producer)
ХранилищеТаблица idempotency_events — 10.01.03 Шаблон спецификации БД сервиса
Уникальное ограничениеUNIQUE (eventId)
СтатусыPROCESSING, PROCESSED, опционально FAILED
Переходы статусов(none) → PROCESSING → PROCESSED; PROCESSING → FAILED при исчерпании retry; FAILED → PROCESSING только по ручному reprocess (роль, audit)
Claim при конкурентной доставкеINSERT … ON CONFLICT DO NOTHING + SELECT; или SELECT FOR UPDATE; см. ветки §3.5 шаг 3
Bounded wait при PROCESSINGТаймаут T, интервал опроса; поведение по истечении — зафиксировать в §3.5 (3b)
TTL записейнапр. 30 дней — housekeeping; не заменяет retention топика и replay
Согласование с retentionTTL таблицы ≥ max(retention топика, окно replay §3.1, SLA reprocess); иначе replay пройдёт как «новое» событие

Сценарии:

СценарийПоведениеCommit/ack
Повтор после PROCESSEDПропускда
Параллельные два consumer на один eventIdОдин claim, второй — ветка 3bтолько у завершившего обработку или при skip по 3b
Падение после эффекта, до PROCESSEDRedelivery: PROCESSING, эффект не повторятьпосле UPDATE PROCESSED
FAILED в DLTЗапись остаётся; повторная delivery без reprocess → skip или DLTпосле DLT, без повторных эффектов
Ручной reprocess FAILED → PROCESSINGПовтор шагов 4–5после PROCESSED

3.8. Побочные эффекты

Заполните по фиче — таблица ниже иллюстрация для примера §4, не обязательный состав.

ДействиеТипПолучательКритичностьПри отказе
Статус идемпотентностиБДPostgreSQLВысокаяretry
УведомлениеREST POSTEmail ServiceСредняяretry → DLT
NotificationSent (опц.)EventKafka notifications.eventsНизкаяlog + metric

3.9. Мониторинг, SLI и алерты

Согласуйте с 10.01.12 Шаблон нефункциональных требований (NFR) и 08.02 Нефункциональные требования (НФТ) — классификация и стандарты: владелец алертов, runbook, целевые SLO.

МетрикаТипSLI / порог (пример)
consumer_messages_totalcounter—
consumer_processed_totalcounter—
consumer_dlt_totalcounterSLO: < 0.1% от processed / 5 min
consumer_laggaugeSLO: lag < 10 000; алерт on-call
consumer_processing_duration_secondshistogramp95 < 5 s
consumer_error_rategaugeалерт > 5% за 5 min
consumer_failed_totalcounterалерт при росте FAILED без reprocess

Трассировка: propagation traceparent / W3C Trace Context и correlationId из headers сообщения — в structured logs и исходящие REST (10.01.12 Шаблон нефункциональных требований (NFR)). Не логировать PII из payload.

Алерты: указать канал (PagerDuty/Slack), владельца команды, ссылку на runbook.

3.10. Версионирование и совместимость

ПолеЧто писать
Стратегия consumerForward compatible: игнор неизвестных полей; default для необязательных
Breaking changeConsumer обновляется до Producer с новым обязательным полем
Процесс03.05.07 Управление версионированием API и жизненным циклом

3.11. Отладка (локально / staging)

Только staging / localhost; без prod credentials. Политика — 08.07 Основы кибербезопасности для системного аналитика.

Kafka: отдельная debug consumer group (не prod group) — сообщения остаются для prod consumer.

kafka-console-consumer --bootstrap-server localhost:9092 \
  --topic orders.events --group order-service-debug --from-beginning

RabbitMQ (management plugin):

Внимание: rabbitmqadmin get снимает сообщение с очереди (ack). Используйте только dedicated test queue, не общую staging/prod очередь. Для интеграционных тестов предпочтительнее publish тестового сообщения.

rabbitmqadmin get queue=orders.notifications.debug ackmode=ack_requeue_false
# или publish тестового сообщения для локального consumer:
rabbitmqadmin publish exchange=orders.topic routing_key=order.created.v1 \
  payload='{"eventId":"...", "eventType":"OrderCreated", ...}'

3.12. Критерии приёмки (QA)

Формат — 02.04.02 Критерии приёмки (Acceptance Criteria). Сценарии ниже — для примера §4; адаптируйте под §3.8.

  • Given валидное OrderCreated → within 5 s эффект выполнен, запись PROCESSED, lag не растёт.
  • Given duplicate eventId → эффект один раз, commit/ack без ошибки.
  • Given invalid JSON → сообщение в DLT/DLQ, offset/ack зафиксирован, consumer_dlt_total++.
  • Given transient 503 внешнего API → retry до N×, затем DLT/FAILED; без двойного эффекта при recovery.
  • Given два параллельных consumer → один эффект на eventId.
  • Given partial batch (Kafka): в батче два offset, первый OK, второй poison → commit только contiguous до первого; второй → DLT без «дыры» в committed offset.
  • Given payload с неизвестным optional полем (forward compatible §3.10) → обработка успешна, поле игнорируется.
  • Given replay (новая consumer group / reset offset) при существующих PROCESSED в TTL → дубликаты не вызывают повторных эффектов.
  • Given rebalance mid-flight (stop consumer pod) → без commit частичной обработки; после redelivery — идемпотентное завершение.

3.13. Примечания и ограничения (NFR)

ТребованиеЗначение
Гарантия обработкиat-least-once + идемпотентность (если не выбран EOS в §3.1)
Параллелизм (Kafka)потоков ≤ партиций топика
max обработки одного сообщения5 s (согласовано с max.poll.interval.ms)
Max размер одного сообщениянапр. 1 MB — лимит payload
Fetch / batch buffermax.partition.fetch.bytes — лимит fetch на партицию (может включать несколько messages); не путать с max размером одного сообщения

4. Пример заполнения

Правило: один документ — Kafka prod. Staging mirror на RabbitMQ — отдельная спека OrderCreatedConsumer-RMQ-staging (ссылка из этой §4 не дублирует §3.2–3.3 RabbitMQ).

Scope: OrderCreatedConsumer в Order Notification Service; Apache Kafka; at-least-once; prod.

Контракт: Producer spec + AsyncAPI orders-events v1.0, channel orders.events, operation onOrderCreated; subject orders.events-value, JSON Schema в Git.

Kafka: топик orders.events (fixed, не pattern), group order-service-notification-group, enable.auto.commit=false, max.poll.records=500, auto.offset.reset=latest, max.partition.fetch.bytes=5 MB.

Политика commit/ack: после шага 5 (PROCESSED); при DLT — commit после publish в orders.events.dlt.

Claim-конфликт: при PROCESSING другим pod — wait 30 s, SELECT каждые 5 s; если PROCESSED — ack; иначе — не ack (redelivery).

Алгоритм (фрагмент): см. §3.5; email POST /email/send с header Idempotency-Key: {eventId}.

Replay: инициатор — platform ops; окно — по заявке; записи PROCESSED в TTL 30 d блокируют повторные эффекты.

Диаграмма последовательности (нотация — 03.02.03 Диаграмма последовательности):

sequenceDiagram
    participant Broker as Kafka
    participant Consumer as OrderCreatedConsumer
    participant DB as PostgreSQL
    participant Email as Email Service

    Broker->>Consumer: Сообщение(ы)
    loop Для каждого сообщения
        Consumer->>Consumer: Десериализация + валидация
        Consumer->>DB: SELECT idempotency_events BY eventId
        alt status = PROCESSED
            Consumer->>Broker: commit offset
        else status = FAILED
            Consumer->>Broker: DLT или skip по runbook
        else не обработано / PROCESSING
            Consumer->>DB: INSERT PROCESSING (claim)
            alt conflict PROCESSING другим worker
                Consumer->>DB: bounded wait + SELECT
                alt стал PROCESSED
                    Consumer->>Broker: commit offset
                else timeout
                    Consumer->>Consumer: не commit — redelivery
                end
            else claim OK
                Consumer->>Email: POST /email/send (Idempotency-Key)
                Email-->>Consumer: 202 Accepted
                Consumer->>DB: UPDATE status=PROCESSED
                Consumer->>Broker: commit offset
            end
        end
    end

5. Связанные артефакты

НаправлениеАртефакт
ВходProducer (10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ)), AsyncAPI/схема (03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf)), BRD/SRS (04.01 Форматы документов (Vision&Scope, BRD, SRS, FRD))
ПараллельноБД (10.01.03 Шаблон спецификации БД сервиса), REST вызовы (10.01.04 Шаблон спецификации REST API), NFR (10.01.12 Шаблон нефункциональных требований (NFR)), ADR при EOS (10.01.13 Шаблон архитектурного решения (ADR))
ВыходМатрица трассировки (10.01.15 Шаблон матрицы трассировки), тест-кейсы (02.04.02 Критерии приёмки (Acceptance Criteria))
Чеклист перед разработкой10.04 Чеклист аналитика перед стартом разработки

6. Связи с другими разделами базы

Темы, которые в §3 даны полями, но не раскрыты как теория:

7. Источники и дополнительные материалы

Документация брокеров

Паттерны и контракты