Как обеспечить попадание всех платежей одного клиента в одну партицию Kafka
Что проверяют
Понимаете ли вы, как продюсер выбирает партицию и где порядок платежей ломается, даже если ключ выбран правильно. Следом обычно спрашивают, что будет при увеличении числа партиций и как не потерять порядок на консьюмере.
Как отвечать
Основа: запись отправляется с ключом, равным идентификатору клиента, new ProducerRecord<>("payments", clientId, payment). Стандартный партиционер Java-клиента для записи с ключом считает murmur2 от сериализованного ключа и берёт остаток от деления на число партиций. Один ключ даёт одну партицию, а внутри партиции Kafka хранит порядок.
Дальше покажите условия, при которых гарантия держится:
- идемпотентный продюсер: с
enable.idempotence=trueпорядок сохраняется при повторах, еслиmax.in.flight.requests.per.connectionне больше 5. В Kafka 3.x это поведение по умолчанию вместе сacks=all, но явная настройка не помешает; - число партиций: после добавления партиций новые платежи клиента уезжают в другую партицию, пока старые ещё не обработаны. Партиции закладывают с запасом сразу;
- один партиционер у всех продюсеров: librdkafka по умолчанию хэширует иначе (
consistent_random), и сервис на Go или Python положит тот же ключ в другую партицию. Для совместимости с Java там ставятmurmur2_random; - сериализация ключа: хэш считается от байтов, поэтому
StringиLongс одним номером клиента дадут разные партиции.
На консьюмере партицию в группе читает один экземпляр, и порядок держится, пока обработка последовательная. Если отдать записи в пул потоков, порядок теряется: задачи надо распределять по ключу. Свой Partitioner для этой задачи нужен редко.
Где спотыкаются
Говорят, что Kafka гарантирует порядок в топике. Порядок есть только внутри партиции.
Не думают о крупных клиентах. Ключ с огромным потоком перегружает свою партицию, и её консьюмер отстаёт от остальных.
Увеличивают число партиций на живом топике и получают платёж, обработанный раньше предыдущего платежа того же клиента.