Messaging-паттерны на собеседовании системного аналитика

Зачем messaging на собесе SA

В микросервисной архитектуре сервисы общаются через сообщения чаще, чем через прямые HTTP-вызовы. Kafka, RabbitMQ, NATS — выбор зависит от паттерна. Системный аналитик должен понимать trade-offs и проектировать message flows.

На собесе системного аналитика messaging спрашивают через сценарии: «как организовать обмен между сервисами», «как сделать exactly-once delivery». Senior SA различает at-least-once vs exactly-once, понимает идемпотентность, dead-letter queues, partitioning.

Synchronous vs asynchronous

Sync (REST / gRPC):

  • Client ждёт ответа
  • Простой mental model
  • Cascade failure: один upstream лёг → все downstream падают

Async (messaging):

  • Publisher отправил → не ждёт
  • Resilience: consumer может быть offline, message ждёт
  • Eventual consistency

Когда async:

  • Critical-path не нужен immediate response
  • High throughput
  • Loose coupling между сервисами

Подробнее — Микросервисы на собесе SA.

Message brokers

Apache Kafka:

  • Distributed log, persistent storage
  • High throughput (millions msg/sec)
  • Partitioning для горизонтального масштабирования
  • Consumers читают по offset
  • Стандарт для event streaming, CDC, аналитики

RabbitMQ:

  • Traditional message broker (AMQP)
  • Queues + exchanges (routing)
  • ACK-based delivery
  • Хорош для job queues, RPC-style messaging

NATS / NATS JetStream:

  • Lightweight, простой
  • Pub-sub + persistence (JetStream)
  • Низкая latency

Redis Pub/Sub / Streams:

  • Для простых cases, fire-and-forget
  • Streams — persistent log (Kafka-light)

Pub-sub vs queues

Pub-sub:

  • Publisher → topic, многие subscribers получают каждое сообщение
  • Kafka: consumer groups для load balancing внутри группы
  • RabbitMQ: fanout exchange

Queue:

  • Producer → queue, один consumer обрабатывает каждое
  • RabbitMQ: классическая queue
  • Используется для job processing, task distribution

В Kafka обе модели через consumer groups: один CG = queue (load balanced), несколько CG = pub-sub.

Delivery semantics

At-most-once:

  • Message может теряться, но не дублироваться
  • Fire-and-forget без ACK
  • Применение: metrics, non-critical telemetry

At-least-once:

  • Message может дублироваться, но не теряется
  • Producer retry + consumer ACK
  • Стандарт в Kafka, RabbitMQ
  • Применение: большинство production scenarios

Exactly-once:

  • Каждое message обработано ровно один раз
  • Хитрая штука: full exactly-once невозможна в distributed system
  • Достигается комбинацией at-least-once + идемпотентный consumer
  • Kafka transactions + idempotent producer — близко к exactly-once в Kafka-only flows

Идемпотентность

Ключевая концепция для надёжного messaging.

Идемпотентный consumer: обработка дубликата = обработка оригинала. F(F(x)) = F(x).

Реализация:

  • Идемпотентный ключ в message (idempotency-key, request-id)
  • Consumer проверяет: «обрабатывал ли я уже этот ключ?»
  • Storage для seen keys (Redis, DB table)
  • TTL для очистки

Пример: payment service. Дублирующий message «charge $100» не должен списать $200. Идемпотентный ключ = order_id → проверка перед charge.

Outbox pattern

Атомарно сохранить data + опубликовать event — невозможно (DB + Kafka = две системы).

Решение:

  1. В одной DB-transaction: business data + event в outbox таблицу
  2. CDC (Debezium) читает outbox → публикует в Kafka
  3. После публикации помечаем consumed

Гарантирует at-least-once delivery от source. Combined с идемпотентным consumer → exactly-once effect.

Подробнее — CDC и event sourcing на собесе DE.

Dead-letter queue (DLQ)

Когда consumer не может обработать message (poison message, bug, downstream сломан) — что делать?

Pattern:

  • Retry N раз с exponential backoff
  • Если не получается — message в DLQ
  • DLQ читают вручную / отдельный consumer
  • Alerts на DLQ growth

В Kafka DLQ — отдельный topic. В RabbitMQ — dead-letter exchange.

Partitioning в Kafka

Topic делится на partitions. Каждая — ordered log. Внутри partition order гарантирован, между — нет.

Partitioning key:

  • Hash от ключа → partition
  • Same key → same partition (order preserved)
  • Пример: user_id → all events for user в одной partition

Зачем:

  • Horizontal scaling: больше partitions → больше parallel consumers
  • Per-key order: важно для events типа user_updated (последний апдейт — финальный)

Анти-паттерн: один «hot» partition (большинство messages с одним ключом) → не масштабируется.

Backpressure

Producer пишет быстрее, чем consumer обрабатывает. Что делать?

Опции:

  • Buffer broker (Kafka) — copes до retention; долгая обработка = lag
  • Drop messages (acceptable для metrics)
  • Throttle producer (rate limit)
  • Scale consumers (auto-scaling)

В Kafka мониторят consumer lag (offset behind latest). Alert если > N → scale.

Типичные вопросы

«Kafka или RabbitMQ?»

Kafka: event streaming, high throughput, persistent log, аналитика. RabbitMQ: task queues, complex routing, RPC-style. Часто оба в одной компании.

«Как обеспечить exactly-once?»

At-least-once + идемпотентный consumer. Идемпотентный ключ в каждом message. Идемпотентность через unique constraint в БД или Redis-проверка.

«Что в DLQ?»

Messages, которые consumer не смог обработать после retries. Причины: bad payload, downstream service down, bug. Action: alert + manual review + fix + reprocess.

«Как организовать обмен заказа между сервисами?»

Order Service → Kafka topic orders.created. Consumers: Inventory (резерв), Payment (charge), Notifications (email), Analytics (BI). Outbox + идемпотентность.

Частые ошибки

  • Sync везде. «Микросервис вызывает микросервис через REST» → cascade failure
  • Без идемпотентности. At-least-once → дубликаты обработаны → BUG в проде
  • Без DLQ. Poison message → consumer лупится → topic stuck
  • Hot partition. Все messages с одним ключом → один consumer перегружен
  • JWT в Kafka message. Sensitive data в plain log — security violation

FAQ

Persistent vs ephemeral messages?

Persistent (Kafka, RabbitMQ durable queue): данные на диске, выживает restart. Ephemeral (Redis Pub/Sub, NATS Core): in-memory, теряется при failure. Production обычно persistent.

Какой retention для Kafka?

Зависит от use case: events для аналитики — 7-30 дней; CDC — может быть compacted (keep last value per key); audit log — годы.

Schema registry нужен?

Для production Kafka — да. Schema evolution без коссистентности → ломаются consumers. Confluent / Apicurio.

Consumer group rebalancing — что это?

Когда consumer присоединяется / отваливается, partitions перераспределяются между members. Stop-the-world pause. Cooperative rebalancing (KIP-429) — incremental.

Streams — embedded library, простой stateful processing. Flink — полноценный stream-processing framework, exactly-once, windowing. Streams для simple, Flink для complex.

Смотрите также