Что такое consumer group в Kafka и как она помогает разделять нагрузку между потребителями?
Что проверяют
Различаете ли вы параллелизм внутри процесса и параллелизм по партициям. У python-разработчика это болезненная точка: потоки упираются в GIL, наращивать пропускную способность приходится процессами, а сколько их запускать, определяет устройство группы.
Как отвечать
Consumer group это потребители с общим group_id, которым брокер раздаёт партиции топика. Партиция достаётся ровно одному участнику группы, значит верхняя граница параллелизма это число партиций, а не число воркеров. Смещения группа хранит на брокере, поэтому перезапуск процесса продолжает чтение с закоммиченной позиции, а другая группа читает тот же топик независимо.
Практический вывод для питона: экземпляр консьюмера не потокобезопасен, его не делят между потоками. Обычная схема это отдельный процесс на партицию или несколько реплик в оркестраторе, либо один асинхронный потребитель на aiokafka, если обработка упирается в сеть, а не в процессор.
consumer = AIOKafkaConsumer(
"payments",
bootstrap_servers="kafka:9092",
group_id="billing-worker",
enable_auto_commit=False,
)
await consumer.start()
async for msg in consumer:
await handle(msg)
await consumer.commit()Где спотыкаются
Говорят, что группа распределяет сообщения. Она распределяет партиции, а сообщения с одинаковым ключом всегда попадают к одному потребителю, и это же даёт порядок в пределах ключа. Если ключей мало и они перекошены, один воркер захлебнётся, а остальные будут скучать.
Считают, что при сбое обработчика брокер передаст сообщение соседу. Нет: об успехе он ничего не знает, соседу достанется вся партиция целиком и только после ребалансировки. Повторы и разбор битых сообщений это забота приложения, отдельной очереди для них Kafka не даёт.
Включают автокоммит и удивляются потерям. Коммит по таймеру подтверждает то, что ещё не записано в базу, и при падении сообщение не вернётся. Коммитить нужно после успешной обработки и заранее считать, что повтор случится, то есть делать запись идемпотентной.