一、Kafka消费者组是什么?
Consumer Group
是Kafka
提供的可扩展且具有容错性的消费者机制。在组内多个消费者实例(Consumer Instance
),它们共享一个公共的ID即 Group ID
。组内的所有消费者协调在一起消费订阅主题(Subscribed Topics
)的所有分区(Partition
)。当然一个分区只能有同一个消费者组的一个Consumer
实例消费。Consumer Group
有三个特性:
Consumer Group
下可以有一个或多个Consumer
实例。 这里的实例可以是一个单独的进程,也可以是同一进程下的线程;Group ID
是一个字符串, 在Kafka集群中唯一标识,Consumer Group
;Consumer Group
下所有实例订阅主体的单个分区,只能分配给组内某个Consumer
实例消费。同一个分区消息可能被多个Group
消费。
二、Kafka消费者组解决了哪些问题?
传统的消息系统中,有两种消息引擎模型:点对点模型(消息队列)、发布/订阅模型
传统的两种消息系统各有优势,我们里对比一下:
- 传统的消息队列模型的缺陷在于消息一旦被消费,就会从队列中删除,而且只能被下游的一个
Consumer
消费。严格的说这不是它的缺陷,
这是它的一个特性。但很显然这种模型的伸缩性(Scalability
)很差,因为下游的多个Consumer
都要“抢”
这个共享消息队列的消息; - 发布/订阅模型,允许消息被多个
Consumer
消费,但它的问题也是伸缩性不高,因为订阅者都必须订阅所有主体的所有分区。
Kafka
为规避传统消息两种模型的缺点,引入了 Consumer Group
机制:
- 当
Consumer Group
订阅多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息; Consumer Group
之间彼此队里,互不影响,它们可以订阅同一组主题而互不干涉。加上Broker
端的消息留存机制,Kafka
的Consumer Group
完美的避开了伸缩性差的问题;kafka
是用Consumer Group
机制,实现了,传统两大消息引擎。如果所有实例属于同一个Group
,那么它实现的就是消息队列模型;如果所有实例分别属于不同的Group
,且订阅了相同的主题,那么它就实现了发布/订阅模型;
三、Consumer Group 实例数量多少才合理?
最理想的情况是Consumer
实例的数量应该等于该Group
订阅主题的分区总数。例如:Consumer Group
订阅了 3个主题,分别是A、B、C
,它们的分区数依次是1、2、3
,那么通常情况下,为该Group
设置6
个Consumer
实例是比较理想的情形。
如果设置小于或大于6
的实例可以吗?当然可以,如果你有3
个实例,那么平均下来每个实例大约消费2
个分区(6/3=2)
;如果你设置了9
个实例,那么很遗憾,有3
个实例(9-6=3
)将不会被分配任何分区,它们永远处于空闲状态。
四、消费位移
消费者在消费的过程中要记录自己消费了多少数据,即消费位置信息,在Kafka
中叫:位移(offset
)。
看上去该Offset就是一个数字而已,其实对于Consumer Group 而言,它是一组KV对,Key是分区,V对应Consumer 消费该分区的最新位移。
老版本的Consumer Group
把位移保存在Zookeeper
中。将位移保存在Zookeeper
外部系统显然好处是减少了Kafka Broker
端的状态保存开销。现在比较流行的提法是将服务器节点做成无状态的, 这样可以自由扩缩容,实现超强的伸缩性。不过在实际使用场景中,发现ZooKeeper
这类元框架并不是适合进行频繁的写更新,而Consumer Group
的位移更新却是一个非常频繁的操作。 这种大吞吐量的写操作极大的拖慢了ZooKeeper
集群的性能,在新版本的Consumer Group
中,Kafka
社区采用了将Consumer Group
位移保存在Broker
端的内部主题中。
五、Rebalance
Rebalance
本质上是一种协议,规定了一个Consumer Group
下所有Consumer
如何达成一致,来分配订阅Topic
的每个分区。比如:某个Group
下有20个Consumer
实例, 它订阅了一个具有100个分区的Topic
。正常情况下,Kafka 平均会为每个Consumer
分配5个分区。这个分配的过程叫Rebalance
。Consumer Group
触发 Rebalance
有三种情况:
- 组成员数量发生变化,比如有新的
Consumer
实例加入组或离开组,抑或是有Consumer
实例崩溃被“踢出”组。 - 订阅主题数量发生变更。
Consumer Group
可以使用正则表达式订阅主题,比如consumer.subscribe(Pattern.complile(“t.*c”))
就表明该Group
订阅所有t
开头,字母c
结尾的主题。在Consumer Group
运行时,新创建一个满足这样条件的主题,那么会触发订阅该主题所有Group
开始Rebalance
。 - 订阅主题分区数发生变化,
Kakfka
一个主题,当分区数增加时,就会触发订阅该主题的所有Group
开启Rebalance
。
Consumer Group
发生Rebalance
的过程:某个 Consumer Group
下有两个Consumer
,比如A
和B
,当第三个成员C
加入时,Kafka
会触发Rebalance
,并根据默认的分配策重新分配A、B、C
分配分区,如下图:
注意:目前Rebalance
的设计是所有Consumer
实例共同参与,全部重新分配所有分区,Rebalance
过程所有Consumer
实例都会停止消费,等待Rebalance
完成。Rebalance
很慢,一个Group
内有几百个Consumer
实例,成功进行一次Rebalance
需要好几个小时。 目前社区没有终极解决方案,最好的解决方案是规避Rebalane
的发生。
参考地址
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员微信号:chengcheng222e
,他会拉你们进群。