deшка
Урок 16.3

Producer: пачки, подтверждения и повторы

Путь отправки события, batching и compression, acks и границы идемпотентного producer.

Producer отвечает за сериализацию, выбор партиции, отправку и обработку результата. Пачки уменьшают накладные расходы, подтверждения определяют границу успеха, а повторы помогают восстановить отправку после сбоя. Эти механизмы решают разные задачи: потерянный ответ ещё не означает, что брокер не сохранил запись.

От приложения до лидера партиции

Producer получает объект, сериализует key и value в байты, выбирает партицию и добавляет запись в буфер. В bootstrap.servers указывают начальные адреса брокеров. Клиент получает метаданные о топиках и лидерах партиций, затем отправляет данные нужному лидеру. Первый доступный адрес помогает войти в кластер; это не постоянный прокси для всех запросов.

Асинхронный вызов отправки часто означает лишь постановку в клиентский буфер. Приложение должно получить итог операции через предусмотренный клиентом future или callback и обработать ошибку. При завершении нужно дать буферу отправиться и закрыть клиент. Немедленный выход процесса после постановки записи в буфер способен оставить её только в памяти.

Например, у источника есть три события e-101, e-102, e-103. Успех добавления всех трёх в локальную очередь producer отличается от трёх успешных ответов брокера. Если окончательная ошибка возникла для e-102, полезно сохранить её идентификатор и причину, а не написать в журнал «пачка отправлена» по факту вызова метода.

Batching уменьшает работу на каждую запись

Batching объединяет записи одной партиции в пачку. Вместо отдельного сетевого запроса на каждый маленький JSON клиент отправляет несколько записей вместе. В одном запросе к брокеру могут находиться пачки разных партиций, но порядок и учёт остаются разделены по партициям.

Три записи одной партиции отправляются отдельными запросами или одной пачкой

Представь 100 записей по 200 байт и условные 100 байт служебных данных на запрос. Отправка по одной потребует 100 × (200 + 100) = 30000 байт. Одна пачка с той же условной накладной стоимостью потребует 100 × 200 + 100 = 20100 байт. Это расчёт на заданной модели, а не измерение реального Kafka-протокола. Он показывает, зачем уменьшать число мелких запросов.

В Java producer batch.size задаёт размер буфера пачки в байтах, а linger.ms — время ожидания дополнительных записей. При наполнении пачки отправка может начаться раньше этого интервала. Значение linger.ms не является верхней границей всей задержки: добавляются ожидание сети, брокера и повторов.

Пример настроек Java producer, не готовая конфигурация сервиса
batch.size=16384
linger.ms=5
compression.type=zstd
acks=all
enable.idempotence=true

В Java-клиенте Kafka 4.3 эти значения batching совпадают с текущими умолчаниями; здесь они записаны явно для разбора. У другого языка и клиента имена и умолчания могут различаться. Увеличивать буферы следует по измерению размера пачек, задержки и памяти. При редких событиях долгое ожидание пачки может ухудшить реакцию.

Compression экономит сеть и хранение

Compression сжимает пачку. Повторяющиеся имена полей и похожие значения дают больше возможностей для сжатия, чем одна маленькая запись. Kafka поддерживает gzip, snappy, lz4 и zstd. Алгоритмы различаются затратами CPU и коэффициентом сжатия, поэтому «лучший» зависит от потока и ресурсов.

Пусть несжатая пачка имеет 20000 байт, а в измерении после сжатия осталось 8000. Передавать нужно 40% исходного объёма. Если producer отправляет по одной записи или данные уже сжаты, выгода может быть меньше. Consumer получает те же логические записи после распаковки; сжатие сохраняет содержимое key и value.

Kafka эффективно использует последовательное добавление в журнал, файловый кеш ОС и передачу пачек. В подходящих режимах путь чтения использует zero-copy, уменьшая лишнее копирование. TLS и другие особенности пути влияют на эту оптимизацию. Высокая пропускная способность складывается из нескольких механизмов; это не обещание фиксированной задержки любого запроса.

Acks задаёт, какого ответа ждёт producer

Acks относится к подтверждению записи брокером. Это отдельное действие от commit позиции consumer.

ЗначениеКогда producer считает запрос успешным
acks=0Не ждёт ответа брокера; результат приёма неизвестен
acks=1Лидер записал в свой локальный журнал
acks=allЗапись подтверждена текущим набором синхронных реплик

При acks=1 лидер может ответить до копирования на follower. Отказ лидера в этом промежутке создаёт риск потери подтверждённой записи. При acks=all участвует набор ISR, а не все когда-либо назначенные копии. Минимально допустимый размер этого набора задаёт min.insync.replicas на стороне топика или брокера; связь параметров подробно изучим в репликации.

Отсутствие ответа оставляет неопределённость: запрос мог не дойти, а мог сохраниться с потерянным ответом. Поэтому клиент выполняет retry, пока позволяет политика и срок доставки. По исчерпании срока приложение получает окончательную ошибку и решает, что делать с событием.

Идемпотентность распознаёт повтор запроса

Идемпотентный producer позволяет брокеру узнавать повторные отправки клиента по идентификатору producer и последовательности записей. Если ответ на первую успешную запись потерялся, retry той же последовательности не добавит вторую копию в журнал.

Ответ на sequence 8 потерялся; повтор той же отправки не создаёт новую запись

Для Java producer идемпотентность требует согласованных настроек: acks=all, разрешённых повторов и max.in.flight.requests.per.connection не больше 5. При отключённой идемпотентности несколько запросов в полёте и retry способны переставить пачки одной партиции: первая не прошла, вторая записалась, первая повторилась позже.

Бизнес-повтор отличается от транспортного. Если приложение дважды вызвало отправку одного event_id как двух новых записей, producer не сравнивает JSON и не объединяет их. Если consumer дважды записал факт во внешнюю базу, producer тоже не управляет этой базой. Для защиты результата нужны механизмы из урока о доставке.

Самопроверка

Проследи четыре этапа: локальный буфер, отправка, подтверждение брокера, запись consumer в приёмник. Объясни, на каком этапе теряется ответ, что исправляет retry и какие повторы остаются задачей приложения.

1. У producer успешно завершился вызов постановки записи в локальный буфер. Что нужно для подтверждения публикации?
2. Ответ брокера потерялся, и идемпотентный Java producer повторил тот же запрос. Какую проблему решает идемпотентность?
3. Что означают batch.size и linger.ms в Java producer?
Войти, чтобы отмечатьдальше →
16.4 →Offsets, commit, replay и lag