← Назад к вопросам

Как сообщения попадают в Kafka

2.7 Senior🔥 41 комментариев
#Клиент-серверная архитектура

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сообщения в Kafka: путь от Producer к Consumer

Это фундаментальный вопрос, который затрагивает архитектуру Kafka и её роль как распределённой системы обмена сообщениями. В основе лежит модель публикации-подписки (pub-sub), где Producer (издатель) публикует сообщения в топики (topics), а Consumer (подписчик) их читает. Давайте детально разберём весь путь.

Ключевые этапы пути сообщения

  1. Создание и отправка сообщения (Producer)
  2. Маршрутизация внутри топика (Partitioning)
  3. Сохранение и репликация (Brokers & Log)
  4. Чтение сообщений (Consumer)

1. Создание и отправка сообщения Producer'ом

Producer — это любое приложение или сервис, которое генерирует данные. Отправляя сообщение, Producer указывает целевой топик. При этом он может (но не обязан) указать ключ (key) и значение (value) сообщения.

// Пример на Java с использованием KafkaProducer
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

Producer<String, String> producer = new KafkaProducer<>(props);

// Ключ (key) важен для определения партиции
ProducerRecord<String, String> record = new ProducerRecord<>(
    "my-topic",          // Название топика
    "user-123",          // Ключ сообщения (опционально)
    "Hello, Kafka!"      // Тело (value) сообщения
);

// Асинхронная отправка с callback для обработки подтверждения
producer.send(record, (metadata, exception) -> {
    if (exception == null) {
        System.out.println("Сообщение записано в партицию " + metadata.partition() + " с offset " + metadata.offset());
    } else {
        exception.printStackTrace();
    }
});
producer.close();

2. Маршрутизация внутри топика: Партиции (Partitions)

Топик в Kafka — это не единая очередь, а логическая группа партиций. Партиция — это упорядоченный, неизменяемый, последовательно дополняемый журнал (log). Именно партиция определяет параллелизм и масштабируемость.

  • Назначение партиции: Решается на стороне Producer. По умолчанию, если указан ключ (key), Kafka использует хеш этого ключа для определения номера партиции (hash(key) % number_of_partitions). Это гарантирует, что все сообщения с одинаковым ключом попадут в одну и ту же партицию, сохраняя порядок для этого ключа.
  • Round Robin: Если ключ не указан (null), сообщения распределяются по партициям по круговому алгоритму (round-robin) для балансировки нагрузки.

3. Сохранение и репликация на Brokers

Брокер (Broker) — это сервер Kafka. Кластер состоит из множества брокеров. Каждая партиция топика хранится на одном или нескольких брокерах (реплицируется).

  • Лидер (Leader) и Последователи (Followers): Для каждой партиции один брокер назначается лидером, а другие (в зависимости от фактора репликации, replication factor) — последователями. Producer отправляет сообщения только лидеру партиции.
  • Запись в Write-Ahead Log (WAL): Лидер записывает сообщение в свой локальный сегмент лога (log segment) на диск. Запись производится в конец лога, и каждому новому сообщению присваивается последовательный смещённый номер (offset) внутри этой партиции. Offset — это уникальный идентификатор сообщения в партиции.
  • Репликация (ISR): Последователи из набора In-Sync Replicas (ISR) постоянно асинхронно или синхронно (в зависимости от конфигурации acks) копируют данные с лидера. Только после успешной репликации на заданное количество реплик (настройка acks у Producer) лидер отправляет Producer'у подтверждение (acknowledgment) об успешной записи. Это обеспечивает отказоустойчивость.

4. Чтение сообщений Consumer'ом

Consumer подписывается на один или несколько топиков и читает сообщения из их партиций.

  • Consumer Groups: Потребители объединяются в группы (consumer groups). Каждая партиция топика в любой момент времени потребляется только одним потребителем из группы. Это позволяет масштабировать обработку: если партиций больше, чем потребителей в группе, одному потребителю будет назначено несколько партиций.
  • Управление смещением (Offset Management): Consumer самостоятельно управляет своим прогрессом, запоминая offset последнего успешно обработанного сообщения в каждой партиции. Он хранит эти offset'ы в специальном внутреннем топике Kafka __consumer_offsets. Это позволяет потребителю перезапуститься и продолжить чтение с того места, где он остановился.
  • Pull-модель: В отличие от многих систем, Consumer явно запрашивает (pull) данные у брокера, а не получает их push-уведомлением. Это даёт потребителю контроль над темпом обработки.
// Пример Consumer на Java
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092");
props.put("group.id", "test-group"); // Критически важный параметр группы
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

Consumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("my-topic")); // Подписка на топик

while (true) {
    // Poll запрашивает данные у брокера
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("partition = %d, offset = %d, key = %s, value = %s%n",
            record.partition(), record.offset(), record.key(), record.value());
    }
    // При автоматической фиксации (enable.auto.commit=true) offset будет сохранён периодически
}

Резюме: Полный путь в виде списка

  • Producer создаёт сообщение с указанием топика и (опционально) ключа.
  • На основе ключа (или алгоритма round-robin) сообщение назначается конкретной партиции внутри топика.
  • Сообщение отправляется лидеру этой партиции, который работает на одном из брокеров кластера.
  • Лидер сохраняет сообщение на диск в свой упорядоченный лог и присваивает ему offset.
  • Сообщение реплицируется на follower'ов из набора ISR для обеспечения отказоустойчивости.
  • После получения подтверждения от нужного числа реплик (acks) лидер подтверждает запись Producer'у.
  • Consumer, входящий в consumer group, опрашивает (poll) брокеров.
  • Consumer получает сообщения из партиций, назначенных ему групповым координатором, и обрабатывает их.
  • Consumer фиксирует (commits) обработанные offset'ы, сохраняя прогресс в топике __consumer_offsets.

Понимание этого процесса критически важно для эффективного тестирования Kafka-интеграций, диагностики проблем с доставкой, латентностью, понимания гарантий доставки (at-most-once, at-least-once, exactly-once) и корректной настройки как продюсеров, так и консьюмеров.

Как сообщения попадают в Kafka | PrepBro