Можно ли в одном сервисе иметь несколько консьюмеров, читающих все сообщения из всех партиций топика в Kafka и как это реализовать
Что проверяют
Понимаете ли вы, как группа консьюмеров делит партиции, и умеете ли получить широковещательное чтение, когда каждому обработчику нужны все сообщения. Следом спрашивают, что будет с офсетами и что изменится при масштабировании сервиса до нескольких подов.
Как отвечать
Начните с механики: внутри одной группы (GroupId) каждая партиция достаётся только одному консьюмеру. Два консьюмера с общим GroupId поделят топик, а не прочитают его дважды. Чтобы каждый видел всё, есть два пути:
- разные группы: у каждого консьюмера свой
GroupId, напримерbilling-auditиbilling-cache. Каждая группа хранит собственные офсеты и получает полный поток. Это основной вариант; - ручное назначение:
consumer.Assign(...)со спискомTopicPartitionOffsetна все партиции, безSubscribe. Ребалансировки нет, но и новые партиции сами не подхватятся: их число узнают черезIAdminClient.GetMetadata, а позицию чтения хранят сами.
По реализации в Confluent.Kafka: IConsumer<TKey, TValue> не потокобезопасен, поэтому один экземпляр на поток. В ASP.NET Core это обычно отдельный BackgroundService на каждого консьюмера. Consume(CancellationToken) блокирующий, поэтому цикл выносят в отдельный поток, чтобы он не держал поток пула. При остановке вызывают Close(): консьюмер корректно выйдет из группы и зафиксирует офсеты.
Сильный ход: спросить, зачем нескольким обработчикам в одном процессе отдельные подключения к брокеру. Часто проще один консьюмер, который раздаёт сообщения обработчикам через System.Threading.Channels.
Где спотыкаются
Запускают несколько консьюмеров с одним GroupId и ждут, что каждый получит все сообщения. Получают раздел партиций, а консьюмеры сверх числа партиций простаивают.
Генерируют GroupId через Guid.NewGuid() при каждом старте. После перезапуска офсеты потеряны, при AutoOffsetReset.Latest пропадут сообщения за время простоя, а на брокере копятся брошенные группы.
Забывают про реплики: если GroupId зашит в код, то при трёх подах каждая группа снова делит партиции между ними. Для рассылки в каждый экземпляр идентификатор группы должен включать стабильное имя экземпляра.