Kafka или RabbitMQ: журнал против брокера
В чём разница между Kafka и RabbitMQ, почему вопрос «что быстрее» поставлен неверно и по какому признаку выбирать. Плюс третий вариант, который часто лучше обоих.
Коротко. Это не два брокера с разной производительностью, а две разные модели. Kafka — журнал, из которого читают и перечитывают; RabbitMQ — брокер, который раздаёт задания и забывает их после подтверждения.
Сравнение обычно начинают с производительности, и это первая ошибка. Kafka и RabbitMQ отличаются не скоростью, а тем, что вообще происходит с сообщением.
Две модели
RabbitMQ — брокер. Сообщение приходит в обменник, по правилам маршрутизации попадает в одну или несколько очередей, потребитель его забирает, подтверждает — и сообщение исчезает. Очередь по природе временна: её содержимое это то, что ещё не сделано.
Kafka — журнал. Сообщение дописывается в конец партиции и лежит там положенный срок хранения независимо от того, прочитал его кто-нибудь или нет. Потребитель держит смещение — свою позицию в журнале. Прочитанное никуда не девается.
Из этого различия следует всё остальное, и различие это не про скорость.
Что даёт журнал
Перечитывание. Смещение можно отмотать назад и обработать всё заново. Выкатили обработчик с ошибкой, испортили данные за сутки — исправили код, сдвинули смещение, пересчитали. В очереди этого нет: сообщения уже подтверждены и удалены.
Много независимых потребителей. Каждая группа держит своё смещение и читает один и тот же поток, не мешая остальным. Добавить пятого потребителя к существующему потоку — операция без последствий для четверых.
Порядок внутри партиции. Гарантируется. Это важно, когда события по одной сущности обязаны примениться в том порядке, в каком произошли.
Цена: партиция — единица параллелизма. Больше потребителей в группе, чем партиций, — лишние простаивают. Число партиций планируется заранее и увеличивается неудобно.
Что даёт брокер
Маршрутизация. Обменники по типу, по образцу ключа, широковещательные. Правило «эти сообщения в очередь A, те в B, а вот эти в обе» описывается конфигурацией, а не кодом потребителя.
Подтверждение по каждому сообщению. Обработчик упал на середине — сообщение вернулось в очередь и досталось другому. В журнале единица учёта — смещение, и «не смог обработать вот это конкретное, но следующее обработал» выражается тяжелее.
Очередь недоставленных. Сообщение, которое не удалось обработать за N попыток, уезжает в отдельную очередь. Встроено.
Приоритеты и срок жизни. Есть.
Цена: подтверждённое сообщение исчезло. Перечитать нечего.
Признак выбора
Один вопрос: сообщение — это задание или событие?
Задание адресовано исполнителю, выполняется один раз и после этого не нужно. «Отправить письмо», «сжать картинку», «пересчитать отчёт». Это очередь, и это RabbitMQ.
Событие — запись о том, что произошло. Оно интересно нескольким подписчикам, и может стать интересно шестому через полгода. «Заказ оплачен», «пользователь зарегистрировался». Это журнал, и это Kafka.
Спор «что быстрее» бессмыслен, потому что при типичных для веб-бэкенда объёмах оба справляются с запасом, а разница проявляется на порядках, которых у большинства проектов нет.
Механика, которая определяет пределы
Три детали, из которых складываются реальные ограничения.
Группы потребителей и партиции. В Kafka партиция закреплена за одним потребителем группы. Партиций восемь — параллелизм не выше восьми, девятый потребитель простаивает. Увеличить число партиций можно, уменьшить нельзя, и увеличение ломает соответствие ключа партиции, а значит и порядок.
Отсюда: число партиций — решение, которое принимают заранее и меняют неохотно. В RabbitMQ такого ограничения нет: потребители тянут из очереди, сколько бы их ни было.
Что именно упорядочено. Kafka гарантирует порядок внутри партиции, а не внутри топика. Сообщения по одному ключу попадают в одну партицию и приходят по порядку; между ключами порядка нет. Если порядок нужен по сущности — ключом должен быть её идентификатор.
RabbitMQ гарантирует порядок в очереди при одном потребителе. Добавили второго ради скорости — порядок кончился.
Срок хранения. В Kafka сообщения удаляются по времени или размеру независимо от того, прочитаны ли они. Потребитель, лежавший дольше срока хранения, теряет данные молча. Это настройка, о которой вспоминают после инцидента.
Эксплуатационная разница
Она весомее функциональной и почти не обсуждается.
Kafka — распределённая система с координацией, репликацией и перебалансировкой групп. Её надо мониторить по отставанию потребителей, следить за размером диска, понимать, что происходит при перебалансировке и почему потребитель встал.
RabbitMQ на одном узле ставится и работает. Кластер сложнее, но и он проще, чем кластер Kafka.
Если у вас нет команды, которая уже умеет эксплуатировать Kafka, это существенная часть цены — и она платится не в момент выбора, а в первую ночь, когда что-то встало.
Третий вариант, о котором забывают
Если поток небольшой, потребитель один и события не нужно перечитывать — очередь в PostgreSQL обыгрывает оба варианта.
SELECT * FROM jobs
WHERE status = 'pending'
ORDER BY created_at
FOR UPDATE SKIP LOCKED
LIMIT 10;
SKIP LOCKED пропускает строки, заблокированные другими читателями, —
то есть даёт конкурентную выборку заданий без внешнего брокера.
Выигрыш не в производительности, а в эксплуатации: нет второй системы, которую надо мониторить, обновлять и чинить ночью. Задание и данные, которые оно меняет, лежат в одной транзакции — исчезает целый класс проблем с рассогласованием, ради которого иначе приходится городить исходящий ящик.
Граница, за которой это перестаёт работать: когда очередь начинает заметно нагружать базу, конкурирующую с рабочим трафиком. До этого момента отдельный брокер — лишняя движущаяся часть.
Что не решает ни один из них
Ни Kafka, ни RabbitMQ не дают однократной обработки. Оба гарантируют доставку «хотя бы раз», и дубликаты лечатся идемпотентностью получателя — разобрано отдельно.
Ни один не спасёт от лавины повторов, если политика повтора настроена без джиттера и бюджета.
Выбор брокера — примерно десятая по важности задача в списке того, что делает обмен сообщениями надёжным. Первые девять — про ваш код.
Где я могу быть неправ. Если Kafka в компании уже стоит, обслуживается и команда её знает, маржинальная стоимость ещё одного топика близка к нулю. Тогда разговор про RabbitMQ или очередь в базе — это разговор про то, чтобы завести вторую систему, и он обычно проигрывает.