一、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
的发生。
参考地址
- https://blog.csdn.net/qq_36918149/article/details/99656156
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员微信号:chengcheng222e
,他会拉你们进群。