RabbitMQ vs Kafka: очереди против лога

RabbitMQ vs Kafka: очереди против лога

Оба называются «брокерами сообщений», но устроены принципиально по-разному. RabbitMQ - это умная очередь с push-доставкой. Kafka - это распределённый лог с pull-доставкой. Неправильный выбор стоит дорого, потому что мигрировать потом тяжело.

Этот трек строится по принципу «сначала практика»: event-driven уже объяснил зачем нужны брокеры. Здесь - как с ними работать.

Модель данных: очередь vs лог

RabbitMQ хранит сообщения в очередях. Когда consumer забирает сообщение и подтверждает (basic.ack), оно удаляется. Каждое сообщение доставляется ровно одному consumer-у из группы (round-robin или при fair dispatch). Push-модель: брокер сам отправляет сообщения consumer-ам.

Producer → Exchange → Queue → Consumer (ack → delete)
                    ↘ Queue2 → Consumer2

Kafka хранит сообщения в партиционированном логе (topic). Сообщения не удаляются после чтения - они хранятся по retention policy (дни, объём). Consumer читает по offset-у в своём темпе. Pull-модель: consumer сам запрашивает следующую порцию.

Producer → Topic/Partition[0] → Consumer Group A (offset 42)
                               → Consumer Group B (offset 17)
         → Topic/Partition[1] → ...

Ключевое отличие: Kafka позволяет нескольким независимым consumer group-ам читать одни и те же события с разных позиций. RabbitMQ - нет.

Exchange и routing в RabbitMQ

RabbitMQ не отправляет сообщения напрямую в очередь. Producer публикует в exchange, exchange решает в какую очередь(и) отправить по routing key.

Типы exchange:

  • direct - точное совпадение routing key (order.created → очередь orders)
  • fanout - во все привязанные очереди (broadcast)
  • topic - паттерн с wildcards (order.*order.created и order.updated)
  • headers - по заголовкам сообщения (редко)
// Объявляем exchange
ch.ExchangeDeclare("events", "topic", true, false, false, false, nil)

// Привязываем очередь с routing key
ch.QueueBind("analytics", "lesson.*", "events", false, nil)
ch.QueueBind("email", "lesson.completed", "events", false, nil)

Это позволяет строить гибкие топологии без изменения producer-а.

Partitions и Consumer Groups в Kafka

Топик в Kafka разбит на партиции. Партиции - единица параллелизма: один consumer в группе читает одну или несколько партиций.

Topic: lesson-events (3 партиции)
Partition 0: msg1, msg3, msg5, ...
Partition 1: msg2, msg4, msg6, ...
Partition 2: msg7, msg8, ...

Consumer Group "analytics" (3 воркера):
  worker-1 → Partition 0
  worker-2 → Partition 1
  worker-3 → Partition 2

Важно: порядок сообщений гарантирован внутри партиции, но не между ними. Если нужен порядок событий пользователя - кладём в одну партицию по user_id как partition key.

Количество consumer-ов в группе не может превышать количество партиций: лишние воркеры простаивают.

Когда выбрать RabbitMQ

RabbitMQ хорош, когда:

Сложная маршрутизация: разные события нужно направлять в разные очереди по разным правилам. Exchange + routing key делает это декларативно.

Работа с задачами (task queue): Celery, фоновые воркеры, cron-задачи. Каждая задача должна выполниться ровно один раз - fair dispatch с ack/nack.

Короткоживущие события: email-уведомления, push. Не нужна история - сообщение выполнено и забыто.

Разнородные consumer-ы: PHP-воркер + Go-воркер + JS-скрипт легко подписываются на один exchange через AMQP.

Publish → exchange → queue "email"    → PHP email-sender
                  → queue "push"     → Go push-sender
                  → queue "analytics"→ JS aggregator

Когда выбрать Kafka

Kafka хороша, когда:

Несколько независимых consumer-ов: лог событий читают аналитика, аудит-сервис, email-сервис и ML-pipeline одновременно, каждый со своим offset-ом.

История событий нужна: compliance, аудит, replay при добавлении нового сервиса. Kafka хранит события дни/недели/бесконечно.

Высокий throughput: Kafka масштабируется до миллионов событий/сек добавлением партиций. RabbitMQ упирается в память раньше.

Event Sourcing: реконструкция состояния из лога событий. Kafka - идеальное хранилище.

Потоковая обработка: Kafka Streams, ksqlDB для обработки событий в реальном времени.

Сравнительная таблица

ХарактеристикаRabbitMQKafka
МодельОчередь (push)Лог (pull)
RetentionДо ackПо времени/объёму
RoutingExchange + routing keyПо топику + partition key
ThroughputТысячи/секМиллионы/сек
Replay событийНетДа (по offset)
Несколько consumer-овНет (fanout - костыль)Да (consumer groups)
ПорядокPer-queuePer-partition
ПротоколAMQP 0-9-1Kafka wire protocol
Go-библиотекаamqp091-gofranz-go / sarama
Docker imagerabbitmq:3-managementconfluentinc/cp-kafka
ZooKeeperНетKRaft (ZK устарел с 2.8)

franz-go vs sarama

Для Kafka в Go два основных варианта:

sarama (Shopify): старейшая библиотека, огромная кодовая база, много legacy. Используется везде, где есть legacy Go-код с Kafka.

franz-go: современная библиотека, написана с нуля. Более простое API, лучшая производительность, поддерживает все современные Kafka API. Рекомендуется для новых проектов.

// franz-go: простой producer
client, _ := kgo.NewClient(
    kgo.SeedBrokers("localhost:9092"),
)
defer client.Close()

record := &kgo.Record{
    Topic: "lesson-events",
    Key:   []byte("user-42"),
    Value: []byte(`{"event":"lesson.completed","lessonId":"go-01"}`),
}
client.Produce(ctx, record, nil)

Типичная ошибка

Выбирать RabbitMQ там, где нужен Kafka, и наоборот. Симптомы неправильного выбора:

  • На RabbitMQ: «нам нужно replay событий» - надо Kafka
  • На Kafka: «нам нужна сложная маршрутизация по 10 типам событий в разные очереди» - надо RabbitMQ или topic per event type
  • На Kafka: «у нас 3 события в сутки» - RabbitMQ проще в ops

Мини-задание

  • Ответь: в твоём проекте есть задачи типа «выполнить один раз» (email, webhook) - это RabbitMQ. Есть аналитика, аудит, несколько читателей - это Kafka
  • Какой тип exchange подойдёт для рассылки одного события в несколько очередей?
  • Если topic имеет 4 партиции, сколько воркеров в consumer group будут реально работать при 6 запущенных?
  • Прочитай: event-driven - гарантии доставки - какие гарантии поддерживает каждый брокер по умолчанию?

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.