kafka某个topic的单个分片为什么只能被一个消费者组内一个消费者消费?
一、概要
- 很多文章只阐述了kafka的分片与消费者组内的消费者为 多对一的关系,并没有说清楚为什么一个分片为什么只能被组内一个消费者消费者,这里讲讲我对这个问题的理解。
二、知识储备
- 当kafka的topic被消费者组订阅时,那么kafka与组内的消费者之间应该构成点对点模式
- 当kafka的topic被多个消费组订阅时,那么kafka与不同组的消费者之间构成发布订阅模式
- kafka使用offset维护消息的消费,存在两种offset:currentOffset 、commitOffset。
- currentOffset由消费者客户端自身维护,消费者每次会根据currentOffset拉取消息,例如某次收到10条消息,那么currentOffset就会设置成10,下次poll消息时就会从11号的数据开始拉取,保证不会重复消费。currentOffset的格式为:topic + partition (currentOffset的存储格式可能不太准确,没有找到权威性的资料)
- commitOffset由kafka服务端维护,消费者poll消息时如果提交currentOffset,那么commitOffset就会进行同步。kafka不会单独为某个消费者设置commitOffset,commitOffset的格式为:topic + partition + groupId。
二、问题分析
1. 分片为什么只能被一个组内消费者消费
- 其实这是一个设计上的问题。当kafka的topic被消费者组订阅时,那么kafka与组内的消费者之间应该构成点对点模式。在这在情况下,一条message是不允许被组内消费者重复消费的。
- 我们假设现在一个分片被两个组内消费者A和B消费,那么为了保证一条message不被A和B重复消费了,kafka就不能让A和B使用currentOffset的方式来读取数据,而是需要让A和B使用一个共同的offset。这时候有人会想,使用commitOffset不就行了吗? 事实上的确可行,但是我认为这样会有以下几种问题:
- commitOffset是存储在kafka 的,那么消费者poll消息时就必须先获取到这个commitOffset,增加了一次网络开销。
- 涉及到了多个线程对同一个offset(临界区,共享数据)进行操作,kafka还必须得维护一套处理并发的机制来保护commitOffset线程安全。
- 如果kafka宕机或者出现异常导致commitOffset丢失,那么所有消费这个分片的消费者会丢失消费进度。
- 基于以上原因,所以分片只能被一个组内消费者消费