Kafka 消费者组是什么?细品消费者组的正邪两面

Kafka 消费者组是什么?细品消费者组的正邪两面

「一面天堂,一面地狱」恐怕是 Kafka 消费者组(Consumer Group)最真实的写照。


作为 kafka 中一个非常有特色的设计,消费者组为 Kafka 实现了 2 种非常典型的消息模型,其强大的伸缩性为Kafka 的牛 x 立下了汗马功劳,但是又因为它“臭名昭著”的再均衡(Rebalance),引出了不少难以琢磨的问题,一正一邪,让人欣喜的同时,又让人痛恨不已。

1. 消费者组面面观

消费者组是什么呢?


消费者组是 Kafka 提供的可扩展且具有容错性的消费者机制。消费者组是一个包含一个或者多个消费者实例的集合。组内的各个消费者具有相同的 ID,这个 ID 称为Group ID。组内的所有的消费者协调在一起共同消费订阅主题(Subsrcibed Topics)的所有分区(Partition)的消息。值得注意的是,某个分区每次只能由组内的一个消费者实例消费。


Kafka 采用的消息模型采用一种改良的发布-订阅模型,Kafka 消息传输过程如下图所示。从图中我们可以看出,一个消费者组共同消费同一个订阅主题,每个分区只能由组内的一个消费者实例消费。

Kafka 消费者组是什么?细品消费者组的正邪两面

图:Kafka 消息传输过程


关于消费者组,我们需要理解以下三个特性:

  • 消费者组包含一个或多个消费者实例。不同的消费者实例可以是一个进程,也可以是同一个进程下的不同线程。
  • Group ID 用于标识消费者组,同时也标识消费者属于哪个消费者组。
  • 同一时刻,消费者组订阅主题的任何一个分区都只能由同一个消费者组中的某一个消费者实例消费。当然,不同的消费者组之间互相独立,互不干涉,消费自由、平等。


那么一个消费者组应该设置多少个消费者实例呢?


由于同一时刻,消费者组订阅主题的任何一个分区都只能由同一个消费者组中的某一个消费者实例消费,因此,一个消费者组中消费者实例数应该小于等于订阅主题的分区数。若消费者实例数大于分区数,多出来的消费者实例就会因为分配不到所要消费的分区而空闲,会造成资源的浪费。最理想的情况是,消费者实例个数刚好等于订阅主题个数,这样消费者组的消费者实例就可以刚好一对一消费订阅主题中的分区。


既然消费者组是由消费者组成的,消费者组就需要管理消费位移,那消费者组是如何管理位移的呢?


在消费者组中,Kafka 采用了将位移保存在 Kafka 内部主题的方法,这个内部主题就是 __consumer_offsets。 Kafka 的消费者组将消费位移保存在 Broker 端的内部主题 __consumer_offsets 中。

2. 消费者组的正与邪

与 Kafka 相比,传统的消息队列有个问题,那就是消息一旦消费,就会从队列中删除,而且只能被下游的一个消费者消费,因此这种模型的伸缩性很差,不能通过扩展消费者实例的个数来增大消费端的消费能力,俗话说的好:“一个好汉三个帮”,没有兄弟帮忙,消费端怎么能提高消费能力呢?而常规的发布-订阅模型倒是允许消息被多个消费者消费,但是它的问题也是伸缩性不高,因为每个消费者都需要订阅主题的所有分区。这种全量订阅的方式既不灵活,也会影响消息的传递效果。这就像是一个相亲的兄弟来相亲会场找女朋友,结果他还需要和其他男生打好关系,这不是多此一举吗?


Kafka 的消费者组很好的解决了上述问题。消费者组订阅了多个不同的主题之后,并不要求组内的每个消费者实例都订阅所有的分区,不同消费者只需要消费其中的部分分区就好了。消息发送到 Broker 端之后,并不会删除,而是将消息持久化,因此可以供多个消费者消费。不同的消费者组之间互相独立,互不干涉,消费自由、平等。


从某种程度上说,Kafka 通过消费者组实现了两种典型的消息模型:如果所有的消费者实例都属于同一个消费者组,那么它实现的就是消息队列模型;如果所有实例属于不同的消费者组,那么它实现的就是典型的发布-订阅模式,因此,Kafka 可以说采用了一种改良的发布-订阅模型。


2.1 消费者组的正


消费者组是

用于实现高伸缩性、高容错性的消费者机制。那消费者组是如何实现高容错性和高伸缩性的呢?


消费者组通过再平衡(Rebalance) 机制实现了高容错性和高伸缩性。Rebalance 本质上是一种协议,规定了一个消费者组下的所有消费者如何分配订阅主题的每个分区。


消费者组内多个消费者实例可以同时读取 Kafka 消息,一旦某个消费者实例“挂了”,消费者组会立即将已经崩溃的消费者移除,并且将其负责的分区交给组内其他的消费者实例来负责,从而保证整个消费者组可以正常消费所订阅的主题,不会丢失消息,这个过程就称为“再平衡”。消费者组通过再平衡机制实现了Kafka的高容错性。


假设某个消费者组有2个消费者,分别是消费者1,消费者2,其中,消费者1负责 topic1-0分区、topic2-1分区和topic3-0分区,消费者2负责topic1-1分区、topic2-0分区和topic3-1分区,但是由于消息量巨大,因此需要引入消费者3。引入之后,就会触发再平衡,并且根据默认的分配策略重新给三个消费者实例分配负责的分区,每个消费者实例负责2个分区。消费者组通过这种再平衡的方式实现了Kafka的高伸缩性。


2.2 消费者组的邪


消费者组的名声正是来自于大名鼎鼎的再平衡,但是,也正是再平衡的“臭名昭著”让消费者组“臭名远扬“。


为什么这么说呢?


首先,再平衡的过程会停止消费者组所有成员的消费过程。在再平衡的过程中,所有的消费者实例全部都会停止消费,stop the world(STW),直到再平衡完成,才会恢复消息进程,这个过程会对消费者组的消费过程造成很大影响。


其次,再平衡的时候所有的消费者实例均需要参与其中,全部重新分配所有分区。这个过程其实做了很多无用功,事实上,尽量减少各个消费者实例所分配的分区的变动才是最优的方案,而不是全部重新洗牌,这样的话,就会节省很多资源和时间。例如,消费者1原本负责消费分区1、2、3,那么再平衡之后也应该接着消费1、2、3,而不是重新分配其他分区,这样的话,消费者1连接这些分区所在的Broker的 TCP 连接就可以继续用了,不仅节省了资源,也减少了时间。


最后,再平衡的速度实在是太慢了。上百个消费者实例的消费者组再平衡一次很可能需要几个小时,这是难以忍受的。


3. 总结

正所谓「成也萧何,败也萧何」,再平衡帮助消费者组实现了高伸缩性、高容错性的消费者机制但是反过来,又严重影响了消费者组的消费过程以及消费速率,造成了很多莫名其妙的bug。


消费者组依靠再平衡机制名扬天下,也因为再平衡机制“臭名远扬“,这个亦正亦邪的消费者组,恐怕也只有我们完全掌握了,才能真正成为一把利刃。


分享到:


相關文章: