> Работали ли вы с брокерами сообщений, такими как Kafka или RabbitMQ (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: ООО Диджитал Сектор Поддержка, Ростелеком

Стек: Python

> Пример ответа

Короткий ответ

Да, работал с Kafka и RabbitMQ в продакшене. Kafka использовал для high-throughput event streaming и log aggregation, RabbitMQ - для task distribution и сложной маршрутизации с гарантированной доставкой. Основной опыт - Python (aiokafka, pika, kombu) и Go. Глубоко разбираюсь в партиционировании, consumer groups, exactly-once semantics, dead letter queues, а также в trade-off между at-least-once и at-most-once.

Подробное объяснение

Kafka - это distributed commit log, оптимизированный под горизонтальное масштабирование и replay событий. Ключевые концепции: topics, partitions, offsets, consumer groups, retention policies. Для Python использую aiokafka для асинхронного consumer/producer, либо confluent-kafka как обёртку над librdkafka - она быстрее и стабильнее.

RabbitMQ - это message broker на основе AMQP, где центральную роль играют exchanges и bindings. Поддерживает несколько routing patterns: direct, topic, fanout, headers. Гарантии доставки настраиваются через publisher confirms и consumer acknowledgements. Для Python - pika (синхронно) или aio-pika (асинхронно).

Основные различия, которые важно понимать:

  • Kafka хранит сообщения на диске и позволяет перечитывать их, RabbitMQ удаляет после подтверждения.
  • Kafka масштабируется через партиции, RabbitMQ - через кластеризацию и quorum queues.
  • Kafka даёт высокую пропускную способность, но сложнее с маршрутизацией, RabbitMQ - гибче в routing, но ниже throughput.
  • Kafka - pull-based, RabbitMQ - push-based (с prefetch control).

На практике

В реальных проектах чаще всего использовал связку: Kafka для event sourcing и streaming pipeline (например, обработка кликов, метрик, CDC), RabbitMQ для command-очередей, где важна гарантированная доставка и retry с exponential backoff.

Типичный паттерн на Python с Kafka:

  • producer отправляет события с ключом для сохранения порядка в партиции.
  • consumer работает в составе consumer group, параллелизм регулируется количеством партиций.
  • обработка идемпотентна, потому что at-least-once может дать дубликаты.
  • для exactly-once использую transactional producer и consumer с isolation.level = read_committed.

С RabbitMQ:

  • настраиваю dead letter exchange для обработки упавших сообщений.
  • использую prefetch=1 для честного распределения нагрузки между воркерами.
  • для долгих задач - manual ack после завершения обработки.

Также приходилось решать проблемы: rebalancing при добавлении consumer, backpressure при медленном consumer, мониторинг lag через Prometheus + kafka_exporter, обработка poison messages.

Пример кода

PYTHON
# Kafka consumer на aiokafka с обработкой ошибок и idempotency
import asyncio
import json
from aiokafka import AIOKafkaConsumer, AIOKafkaProducer
async def process_event(event):
# идемпотентная обработка: проверяем по ключу в БД
pass
async def consume():
consumer = AIOKafkaConsumer(
'user_events',
bootstrap_servers='localhost:9092',
group_id='user-events-processor',
enable_auto_commit=False,
auto_offset_reset='earliest',
max_poll_records=500,
)
await consumer.start()
try:
async for msg in consumer:
try:
event = json.loads(msg.value)
await process_event(event)
await consumer.commit()
except Exception:
# логируем и отправляем в DLQ
await producer.send('user_events_dlq', msg.value)
finally:
await consumer.stop()
# RabbitMQ consumer на aio-pika с manual ack
import aio_pika
async def on_message(message: aio_pika.IncomingMessage):
async with message.process(requeue=False):
try:
await handle_task(message.body)
except Exception:
# отправка в dead letter exchange
await message.channel.default_exchange.publish(
aio_pika.Message(body=message.body),
routing_key='tasks_dlq',
)
async def main():
connection = await aio_pika.connect_robust("amqp://guest:guest@localhost/")
channel = await connection.channel()
await channel.set_qos(prefetch_count=1)
queue = await channel.declare_queue("tasks", durable=True)
await queue.consume(on_message)

Как отвечать на собеседовании

Начни с конкретики: где именно использовал, какие задачи решал, какой объём данных. Затем переходи к архитектурным решениям: почему выбрал Kafka или RabbitMQ, какие альтернативы рассматривал. Обязательно упомяни гарантии доставки и как их обеспечивал. Покажи понимание внутреннего устройства: партиции, offset, consumer group, ack, prefetch. Если спросят про сравнение - приведи конкретные метрики из своего опыта. Хорошо добавить пример проблемы, которую решал: потеря сообщений, дубликаты, рост lag, rebalancing.

Что проверяет интервьюер

  • Понимание разницы между message queue и event streaming.
  • Умение выбирать инструмент под задачу, а не просто перечислять фичи.
  • Знание гарантий доставки и их практических последствий.
  • Понимание масштабирования и параллелизма.
  • Опыт решения реальных проблем в проде.
  • Владение Python-экосистемой для работы с брокерами.

Типичные ошибки

  • Путаница между at-least-once и exactly-once, заявления "у нас exactly-once" без объяснения, как это реализовано.
  • Непонимание, что Kafka не удаляет сообщения после чтения, и как работает retention.
  • Ответ "RabbitMQ лучше, потому что проще" без учёта требований к throughput и replay.
  • Игнорирование проблемы rebalancing и её влияния на порядок обработки.
  • Утверждение, что Kafka гарантирует порядок в топике - порядок гарантируется только внутри партиции.
  • Отсутствие упоминания про idempotency при at-least-once.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью