Брокеры сообщений

Брокеры сообщений #

1. Плюсы и минусы брокеров сообщений #

Плюсы

  • Асинхронность - службы могут работать независимо друг от друга
  • Масштабируемость - легко увеличивать количество потребителей
  • Надёжность - сообщения не теряются благодаря механизмам повторной отправки и подтверждений
  • Разделение нагрузки - потребители могут обрабатывать сообщения параллельно

Минусы

  • Сложность настройки - требует правильной конфигурации для эффективной работы
  • Задержка - добавление брокера увеличивает задержку доставки сообщения
  • Поддержка инфраструктуры - нужны усилия для мониторинга и администрирования


2. Разница между Kafka и RabbitMQ #

КритерийKafkaRabbitMQ
Модель обменаПубликация-подписка (pub-sub)Точка-точка (point-to-point), публикация-подписка
Хранение сообщенийСообщения хранятся на диске до истечения срока храненияУдаляются после прочтения (по умолчанию)
Обработка сообщенийPull-модель, потребители запрашивают сообщенияPush-модель, сообщения “проталкиваются” потребителям, когда те готовы их принять
ПроизводительностьВышеНиже
МасштабируемостьЛегко масштабируется горизонтально за счёт распределённой архитектурыМасштабируемость ограничена по сравнению с Kafka, хотя можно использовать кластеризацию


3. Что такое топик в Kafka? #

Топик в Kafka - это логический канал передачи сообщений. Состоит из разделов (партиций), которые представляют из себя FIFO-структуру. Разделение топика на разделы позволяет распределять нагрузку между брокерами, и обрабатывать их параллельно, обеспечивая масштабируемость и отказоустойчивость


4. Что такое Producer и что такое Consumer? #

Producer (производитель) - отправитель сообщений Consumer (потребитель) - получатель сообщений

Что даст увеличение количества консьюмеров? Увеличение числа консьюмеров в группе (consumer group) позволяет ускорить обработку данных, т.к. каждый консьюмер будет обрабатывать свою партицию одновременно с остальными консьюмерами. Также это позволяет распределить ответственность между разными компонентами.

Если консьюмеров меньше, чем партиций - они будут распределены через Round-Robin (или кастомный метод). Если консьюмеров больше, чем партиций - какие-то из консьюмеров будут простаивать

Пример Топик с 4 партициями

  • Если в группе 2 потребителя, то каждый будет обрабатывать 2 партиции
  • Если в группе 3 потребителя, то (по дефолту) один из них будет обрабатывать 2 партиции
  • Если в группе 4 потребителя, каждый получит по 1 партиции
  • Если в группе 5 потребителей, один из них останется без работы


5. Что такое partition в Kafka? #

Partition (партиция, раздел) - раздел топика, структура данных типа FIFO без возможности удаления элемента при чтении.

На что делится partition? Партиция не делится дальше, содержит записи/сообщения.

Запись/сообщение в партиции представляет из себя:

  • (опционально) Key - ключ, используется для распределения сообщения по партициям, не обязан быть уникальным
  • Value - данные/информация
  • (опционально) Headers - заголовки, используются для передачи метаданных, которые по тем или иным причинам не могут быть занесены в key-value
  • Timestamp - метка времени (создания или добавления в партицию в зависимости от настроек)


6. Распределение сообщений. Как сообщения распределяются по партициям? #

Продюсер определяет, в какой раздел отправить сообщение, используя следующие подходы:

  1. Явное указание партиции - продюсер может явно указать, в какую партицию отправить сообщение

  2. Выбор партиции на основании алгоритма (без явного указания)

    • Если сообщение содержит ключ, Kafka использует хеширование ключа для выбора раздела. Это гарантирует, что все сообщения с одинаковым ключом будут попадать в один раздел
    • Если ключ и явное указание партиции отсутствуют, используется стратегия Sticky Partitioning - все сообщения пишутся в одну партицию до достижения batch.size (размер батча в байтах, по умолчанию равен 16384)

    Примечание. Распространено заблуждение, что по умолчанию Kafka-продюсер распределяет сообщения по партициям, используя стратегию Round-Robin. Однако с версии 2.4 это не так ( документация). Выбрать стратегию Round-Robin можно при помощи явного указания

    spring:
      kafka:
        producer:
          properties:
            partitioner.class:
    	        org.apache.kafka.clients.producer.RoundRobinPartitioner
    
  3. Кастомный алгоритм - продюсер может реализовать собственную логику распределения сообщений по разделам (реализовав интерфейс Partitioner)


7. Гарантии доставки в Kafka #

  • At least once - сообщение будет доставлено как минимум один раз (может больше)
  • At most once - сообщение будет доставлено максимум один раз (может не быть доставлено вовсе)
  • Exactly once - сообщение будет доставлено ровно один раз. Это наиболее строгий уровень гарантии и требует согласованности между producer-ом и consumer-ом, чтобы избежать дублирования сообщений

Хабр. Философские размышления на тему exactly once


8. Какие существуют паттерны работы с очередями? #

  • Point-to-Point (P2P) - один продюсер, один потребитель. Сообщение читается одним потребителем и удаляется из очереди
  • Publish-Subscribe (Pub/Sub) - один продюсер отправляет сообщение нескольким подписчикам через обменники
  • Work Queue - несколько потребителей обрабатывают сообщения из одной очереди, распределяя нагрузку
  • Dead Letter Queue (DLQ) - очередь для сообщений, которые не удалось обработать
  • Priority Queue - сообщения обрабатываются в зависимости от их приоритета


9. Основные компоненты Kafka? #

Apache Kafka - это распределённая система обработки потоков, состоящая из нескольких ключевых компонентов

  • Producer (производитель) - отправляет данные в Kafka
  • Consumer (потребитель) - получает данные из Kafka
  • Topic (топик) - логическая сущность, разделяющая данные на каналы
  • Partitions (партиции) - разделы топика, которые разделяют данные для их распределенного хранения и параллельной обработки. Партиции дают возможность хранить данные на разных брокерах
  • Broker (брокер) - сервер, ответственный за хранение данных в Kafka
  • Consumer Group (группа потребителей) - объединение консьюмеров, которые параллельно обрабатывают данные одного топика. Каждый консьюмер в группе обрабатывает разные партиции топика, что улучшает параллельность и производительность
  • Controller - один из брокеров, который управляет метаданными кластера и отвечает за перераспределение партиций в случае отказа
  • ZooKeeper - координатор, служит для управления и координации брокеров, поддержания метаданных системы. В новых версиях Kafka (с 3.7) вместо него используется KRaft (Kafka Raft). С версии 4.0 Zookeper удалён


10. Kafka. Consumer Group #

Consumer Group - это механизм для группировки консьюмеров, работающих с одним топиком, для:

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


11. Из чего состоит Kafka кластер? #

Kafka-кластер — это распределенная система для передачи, хранения и обработки сообщений. Его основные компоненты:

КомпонентОписание
Брокеры (Brokers)Серверы, которые хранят данные и обрабатывают запросы производителей (producers) и потребителей (consumers). Каждый брокер имеет уникальный ID.
Топики (Topics)Логические категории для сообщений. Сообщения внутри топика распределены по разделам (partitions).
Разделы (Partitions)Логическое деление топика для масштабируемости. Сообщения в разделе упорядочены, между разделами порядок не гарантируется.
Реплики (Replicas)Копии разделов, хранящиеся на других брокерах для отказоустойчивости.
Контроллер (Controller)Один из брокеров, который управляет метаданными кластера и отвечает за перераспределение разделов в случае отказа.
Производители (Producers)Клиенты, которые публикуют сообщения в топики.
Потребители (Consumers)Клиенты, которые читают сообщения из топиков, обрабатывая данные в зависимости от задачи.
Consumer GroupГруппа потребителей, которые совместно обрабатывают сообщения из одного топика, разделяя нагрузку.
Zookeeper / KRaftСервис координации для управления метаданными кластера и состоянием брокеров (Zookeeper заменяется на KRaft в новых версиях Kafka).


12. Брокеры сообщений vs Rest #

ХарактеристикаБрокеры сообщений (Kafka, RabbitMQ)REST API
Модель коммуникацииАсинхронная (очереди, топики)Синхронная (запрос-ответ)
Скорость передачиВысокая, оптимизирована для работы с большими объемами данныхЗависят от производительности HTTP сервера
Надежность доставкиПоддерживает подтверждение (ACK), ретрансляцию сообщенийОбычно не предоставляет механизмов ретрансляции
МасштабируемостьВысокая, благодаря горизонтальному масштабированию брокеровТребует отдельных решений для масштабирования
Обработка данныхГибкая, подходит для потоковой обработки данныхПодходит для CRUD-операций
Сложность настройкиТребует установки и управления инфраструктуройЛегче развернуть, использует стандартный HTTP


13. Pull и push модели, в чём разница? #

Pull-модель (”вытягивание”) - клиент запрашивает данные у сервера (или брокера). Пример - консьюмеры в Kafka запрашивают данные у брокеров Преимущества:

  • клиент контролирует скорость обработки, что особенно важно при высокой нагрузке.
  • легко обрабатывать данные на уровне клиента Недостатки:
  • может возникнуть задержка, если клиент редко запрашивает данные
  • требует постоянного запроса данных

Push (”проталкивание”) - сервер отправляет данные клиенту по мере их поступления. Пример: WebSocket, Firebase Cloud Messaging. Преимущества:

  • меньшая задержка - данные сразу отправляются клиенту
  • удобно для реального времени (например, уведомления) Недостатки:
  • клиент может быть перегружен большим потоком данных
  • контроль над потоком находится на сервере


14. Как гарантировать в Kafka идемпотентность, чтобы не было задвоений и потерь сообщений? #

Идемпотентность в Kafka означает, что одно и то же сообщение не будет записано несколько раз, даже если производитель (producer) повторно отправляет его из-за сбоев

Механизмы обеспечения идемпотентности:

  1. Предотвращение дублирования сообщений - включение идемпотентности у продюсера: Параметр enable.idempotence в конфигурации продюсера позволяет Kafka отслеживать дубликаты сообщений с использованием уникального идентификатора производителя (Producer ID) и последовательного номера сообщения (Sequence Number)
    Properties props = new Properties();
    props.put("enable.idempotence", "true");
    
  2. Предотвращение потерь:
    • Ретрай на уровне продюсера - в конфигурации продюсера по умолчанию включена повторная отправка сообщений в случае отсутствия подтверждения от брокера (acks)
    • Репликация и уровень подтверждений - установка acks в "all" гарантирует запись сообщения во все ISR (InSync Replica) реплики


15. Как в Kafka гарантируется последовательность сообщений? #

Сообщения в одной партиции записываются последовательно. Kafka гарантирует, что потребитель получит их в том же порядке, в котором они были записаны. Kafka не гарантирует порядок сообщений между разными партициями топика, так как каждый раздел обрабатывается независимо


16. Что сделать, чтобы сообщения попадали в одну партицию? #

  • Использовать одну партицию
  • Использовать ключ партиционирования (key)
  • Использовать кастомный метод партиционирования
    • Реализовать собственный Partitioner, чтобы задавать логику распределения
      public class CustomPartitioner implements Partitioner {
          @Override
          public int partition(String topic, 
      				            Object key, 
      				            byte[] keyBytes, 
      				            Object value, 
      				            byte[] valueBytes, Cluster cluster) {
              // Кастомная логика выбора раздела
              return somePartitionIndex;
          }
      }
      
    • Зарегистрировать его в конфигурации:
      spring:
        kafka:
          producer:
            properties:
              partitioner.class:
      	        org.apache.kafka.clients.producer.RoundRobinPartitioner
      
  • Использовать прямое указание номера партиции при отправке
    producer.send(new ProducerRecord<>("topic", 0, null, "value"));
    


17. Как работает Round Robin? #

Round Robin - это стратегия распределения данных, при которой сообщения равномерно отправляются в разные разделы топика

Логика:

  1. Производитель отправляет первое сообщение в первую доступную партицию
  2. Следующее сообщение отправляется в следующую партицию, и так далее
  3. После достижения последнего раздела цикл повторяется с первой партиции

Пример: Топик имеет 3 партиции:

Partition 0: Msg1, Msg4
Partition 1: Msg2, Msg5
Partition 2: Msg3, Msg6


18. Acknowledgment в Kafka #

Acknowledgment (acks) - это механизм подтверждения записи сообщения в Kafka. Производитель получает подтверждение от брокера после записи сообщения

Типы acks:

  • acks = 0:
    • производитель не ждет подтверждения
    • самая быстрая, но ненадежная стратегия (возможна потеря сообщений)
  • acks = 1:
    • производитель ждет подтверждения от лидера-реплики
    • сообщение считается доставленным, если лидер записал его в лог
    • возможна потеря сообщений, если лидер сбоит до синхронизации реплик
  • acks = all (или -1):
    • производитель ждет подтверждения от всех реплик ISR, включая лидера
    • максимальная надёжность - сообщения не теряются, если хотя бы одна реплика доступна
    • подходит для критичных данных


19. Смещение (offset) в Kafka #

Offset - это уникальный идентификатор сообщения в рамках одного раздела. Он представляет собой смещение (индекс с 0) сообщения в логе брокера

Использование:

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

Виды управления:

  • Автоматическое управление (auto commit) - Kafka сама обновляет offset после чтения сообщения, настраивается параметром enable.auto.commit=true
  • Ручное управление (manual commit) - потребитель сам обновляет offset после успешной обработки сообщения, используется для большей надежности
    	consumer.commitSync(); // Обновление offset синхронно
    	consumer.commitAsync(); // Обновление offset асинхронно
    


20. Повторное чтение данных из Kafka #

Kafka позволяет читать данные повторно, так как сообщения не удаляются сразу после прочтения

Как прочитать данные повторно?

  1. Сбросить offset у консьюмера - Kafka хранит offset (позицию в топике) каждого консьюмера, можно сбросить offset назад и прочитать сообщения повторно
    kafka-consumer-groups --bootstrap-server localhost:9092 --group my-group 
    --reset-offsets --to-earliest --execute
    
  2. Использовать enable.auto.commit=false - если offset не фиксируется автоматически, консьюмер будет читать данные заново при перезапуске
        spring:
    	    kafka:
    		    consumer:  
    			  enable-auto-commit: true
    
  3. Читать из с конкретного offset
    	consumer.seek(
    	    new TopicPartition("test-topic", 0),
    	    42
    	);
    


21. DLQ topic? #

Dead Letter Queue (DLQ) - это отдельный Kafka-топик, куда отправляются сообщения с ошибками

Зачем нужен DLQ?

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

Как настроить DLQ?

  • Вручную отправлять сообщения в DLQ
    try {
        processMessage(record);
    } catch (Exception e) {
        ProducerRecord<String, String> dlqRecord =
            new ProducerRecord<>("dlq-topic", record.key(), record.value());
        kafkaProducer.send(dlqRecord);
    }
    
  • Использовать Kafka Connect DLQ - если используется Kafka Connect, можно настроить DLQ автоматически:
    	errors.deadletterqueue.topic.name=dlq-topic
    	errors.deadletterqueue.context.headers.enable=true