Skip to content

Очереди / Брокеры сообщений

Термины

  • Гарантия доставки - дошло ли сообщение до получателя
  • Надежность - сохранность данных/конфига после падения
    • Обычно подразумевается сохранение на диск и репликация
  • Сообщение - байты (строка/json/что угодно) + метадата (заголовки/приоритет)

Модели Брокеров

  • Push-модель - сообщения отправляются получателю
  • Pull-модель - получатель сам ходит за сообщениями

Гарантии доставки

  • At-most-once delivery - 0-1
    • Отравили и забыли - нет гарантии успешной обработки
    • Быстрее, больше пропускная способность
  • At-least-once delivery - 1+
    • Отправили сообщение, но подтверждение не получили, значит отправляем еще раз
    • Важно обрабатывать такой кейс, напр. с помощью идемпотентности
  • Exactly-once delivery - 1
    • Аналог транзакций

Rabbit MQ

Архитектура

  • Паблишер > Exchange > Очередь > Консумер
    • Паблишер - отравляет/создает сообщения
    • Exchange - роутер сообщений от паблишеров в очереди
    • Очередь - хранит сообщения + отправляет Консумерам
    • Консумер - обрабатывает сообщение
  • Очередность - FIFO - для 1 паблишера - 1 консумера

Exchange

  • Сущности роутинга
    • Routing Key - ключик (просто строка), на основе которой роутим
    • Binding - связь между Exchange и Exchange/Queue
  • Виды Exchange роутинга
    • Fanout - во все Очереди и Exchangeи по биндингу
      • Механизм Pub/Sub
      • Routing Key игнорируется
    • Direct - полное совпадение Routing Key
    • Topic - direct + wildcard
    • Header - по заголовкам
    • Consistent Hashing - хеширует ключ и отправляет только в одну очередь
  • Dead letter exchange - Exchange с недоставленными сообщениями
    • Если сообщение не доставлено / очередь переполнена, то можно вернуть сообщение из очереди обратно в спец эксченж

Доставка / гарантии / надежность

  • At-least-once, At-most-once
  • Паблишер отправляет сообщение > rabbit что-то поделал с сообщением > отправляет паблишеру команду ack
  • Если паблишер не получил ack (кейс 500), сообщение отправляется еще раз, предварительно возвращая сообщение в очередь
  • Очередь >-сообщение-> Консумер ⇒
    • Консумер >-ack-> Очередь ⇒
      • Сообщение удаляется из очереди
      • Иначе повторная доставка / доставка другому консумеру
  • Можно из паблишера сразу в очередь - дефолтный эксченж
  • Надежность: Можно включить флаг durable, тогда конфиг будет сохраняться

Масштабирование

  • 1 очередь - несколько консумеров ⇒
    • 1 сообщение - 1 консумеру
    • несколько сообщений равномерно распределяется по всем консумерам
  • При добавлении новых консумеров никаких пауз нет
  • Можно ждать ack, тогда будет медленее, можно не ждать, тогда будет быстро
    • QoS - лимит на кол-во неподтвержденных сообщений

Kafka

Архитектура

  • Распределенный реплицируемый лог
    • Распределенный - на разных машинах - партиции
    • Реплицируемый - копирует сообщения - реплики
    • Лог - упорядоченный журнал событий = файл со строками
  • Продусер > Топик / Партиция <-Оффсет-< Consumer (Group)

  • Продусер всегда пишет в конец партишена

Топик / Партиция

  • Топик - набор событий одного или нескольких типов
    • Важно не делать много топиков
  • Топик - набор партиций
    • Важно писать равномерно по всем партициям
  • Партиционирование - выбор партиции
    • key > hash > %len(partitions) > partition
    • Это происходит на стороне producer ⇒ можно свой алго написать
  • Важно определить сколько партиций надо

Consumer / Offset

  • Consumer Group = логический Consumer
  • Консумер читает партицию с последнего места чтения
  • Можно читать, то что уже прочитано - тайм-тревел
  • Оффсет может храниться у консумера и у Кафки (если консумер не хранит оффсет)
  • key - аналог routing-key в rabbit
  • 1 партиция = 1 логический консумер
    • Т.е можно 2 партиции - 1 консумер, нельзя - 1 партиция - 2 консумера
    • Важно что логический - если консумеры будут разными приложениями, то чтение из одной парции это ок
    • Group Coordinator - kafka-брокер, который следит за группой
    • Group Leader - консумер, который распределяет консумеров по партициям
      • Можно управлять алго
  • commit offset - команда фиксации нового оффсета
    • Консумер > Kafka > _consumer_offsets Topic
    • Нужно для уведомления kafka
    • Иначе, если консумер сдох, то kafka будет отдавать сообщения с послндего сохраненного оффсета

Масштабирование / Перебалансировка

  • При добавлении нового консумера чтение сообщений приостанавливается до конца обработки текущих сообщений, а затем партиции распределяются заново

Репликация

  • Тупа копирование партиций на несколько нод
  • Параметр - factor - желательно 3+

Доставка и Гарантии

  • Последовательность: Если сообщение B пришло после А, значит Offset B > Offset A
  • Сообщение закоммичено, когда доставлено во все реплики = sync replica
    • Можно отключить
  • Сообщения не будут потеряны если жива хотя бы одна реплика
  • Параметр acks
    • acks = 0 - “выстрелил и забыл”
    • acks = 1 - хотя бы одна реплика (лидер) сохранила на диск
    • acks = all - все реплики получили сообщение

Pull-модель

  • консумеры сами запрашивают сообщения у Kafka
  • Kafka отдает сообщения батчами (пачками)
  • latency хуже, потому что Kafka ждет мб еще сообщения придут
  • Добавление новых партиций ломает очередность

Хранение

  • Если хранить вечно не надо (место на диске не резиновое), то можно настроить время жизни / макс. размер

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

  • Ждем
    • Но это плохо
  • Таймауты / Ретраи / Алертинг
  • Заглушка
    • Сохранение частичной инфы в бд, обработаем потом, в другом событии
  • Переместиться во времени
  • Переложить сообщение
  • Идемпотентность

Что выбирать?

Kafka

  • Высокая пропускная способность, но хуже latency
  • Хранение сообщений + возможность перечитать сообщения
  • Репликация, но и сложнее настройка

Rabbit

  • Лучше latency
  • Роутинг

Redis

  • Скорость, простота
  • Нет гарантий доставки
  • Нет роутинга

Материалы