作为一份笔记,本文再次梳理一下 Kafka 的 Consumer Group。我们知道,一个 Topic 往往会有多个 Partition,一条消息只会被写到一个 Kafka 的 Partition 中,那 Consumer 是怎么消费 Message 的呢? Consumer Group 又从中起到了什么作用呢?
Consumer Group 与 Consumer 到底有何不同?
首先,我们必须要非常清楚地明确一点:Consumer Group 才是消费一个 Topic 的“独立单位”,什么意思呢?就是说:一个 Consumer Group 才是一个通常意义上和 Consumer Client,它下面的 Consumer 实例是作为一个整体消费且只消费一遍 Topic 里的消息,而不是每一个 Consumer 独立消费一遍。当然,如果一个 Consumer Group 只有一个 Consumer,就没有区别了。
这样的话,一个 Consumer Group 下的多个 Consumer 是怎么分配消息的呢?一条消息投递到 Kafka 中究竟会被哪个 Consumer 读取?这中间的具体逻辑是什么呢?下图给出个非常清晰的解释:(摘抄自 Kafka The Definitive Guide, 2nd Edition ):
下图同理:
上图中 Consumer Group 中有三个 Consumer 实例,Topic 中有四个分区,Kafka 的安排是:其中两个 Consumer 各自读取一个 Partition,剩下的一个 Consumer 读取其余两个 Partition。所以,你会看到:没有一个 Consumer 能读全所有的消息!大家更像是在分头消化各自的“责任田”。下图是以两个 Consumer Group 为例,再次描述了同样的逻辑:
下面的两张图依然是描述了这个事实:
根据 Consumer Group 的工作特点,我们不难看出:
-
一个 Partition 只会被一个 Consumer Group 中的一个 Consumer 实例消费 (不会有同 Group 的两个 Consumer 重复消费一条消息)
-
一个 Consumer Group 的 一个 Consumer 实例可能会消费一个 Partition,也可能会消费两个或两个以上的 Partition,还有可能一个 Partition 都不消费,这取决于 Partition 和 Consumer 的数量!以下是我们说的三种可能情形的示意图:
- Case 1:一个 Consumer Group 只有一个 Consumer,该 Consumer 能读取到全部消息
- Case 2:一个 Consumer Group 有多个 Consumer,Consumer 均分 Partition,每个 Consumer 都读取一部分消息
- Case 3:一个 Consumer Group 的 Consumer 数量超过了 Partition 的数量,部分 Consumer 因未分配到 Partition,一条消息也不会读到
Kafka 为什么要设计 Consumer Group 而不是直接以 Consumer 为单位读取 Message?
Kafka 设计 Consumer Group 的意图应该不难揣测,这样做显然是让 Topic 从只能由一个 Consumer 消费变成了:可以多个 Consumer 消费并行消费且还不用担心有重复数据,这样能提升消费端的整体吞吐能力。不过,要注意的是:Kafka 只能保证单个分区内的消息是按产生的先后顺序排列的,在多分区下,无法从整体上保证消息的有序性,所以,当一个 Consumer Group 下有多个 Consumer 一起读取消息时,是不能保证消息的时序性的!
另外由于kafka目前只提供单个分区内的消息顺序,而不会维护全局消息顺序,因此如果用户要实现topic全局消息顺序,就只能通过让每个consumer group下只包含一个consumer实例的方式来间接实现的。
Consumer Group 的相关操作
1. 列出某个 Topics 下的所有 consumer group
kafka-consumer-groups.sh --bootstrap-server $KAFKA_BOOTSTRAP_SERVERS --list
2. 查看某个 Consumer Group 的详细信息
kafka-consumer-groups.sh --bootstrap-server $KAFKA_BOOTSTRAP_SERVERS --describe --group console-consumer-76923
3. 重置偏移量
kafka-consumer-groups.sh --bootstrap-server $KAFKA_BOOTSTRAP_SERVERS --topic 'osci.mysql-server-3.inventory.orders' --group console-consumer-76923 --reset-offsets --to-earliest --execute
小结
最后,我们再简单总结一下:
Kafka中的每一个Consumer都归属于一个特定的Consumer Group,如果不指定,那么所有的Consumer都属于同一个默认的 Consumer Group。Consumer Group 由一个或多个Consumer组成,同一个Consumer Group中的Consumer对同一条消息只消费一次。每个 Consumer Group 都有一个唯一的ID,即Group ID,也称之为 Group Name。Consumer Group 内的所有 Consumer 协调在一起订阅一个Topic的所有Partition,且每个Partition只能由一个 Consumer Group中的一个Consumer进行消费。
参考资料
https://www.luozhiyun.com/archives/260
https://www.conduktor.io/kafka/kafka-consumer-groups-and-consumer-offsets/
https://medium.com/javarevisited/kafka-partitions-and-consumer-groups-in-6-mins-9e0e336c6c00