消费组,即Consumer Group ,应该算是kafka比较有创意的设计了。那么何谓ConsumerGroup呢?用一句话概括就是:ConsumerGroup是kafka提供的可扩展且具有容错性的消费者机制。既然是一个组,那么组内必然可以有多个消费者和消费者实列,他们共享一个公共的ID,这个ID被称为GroupID。组内的消费者协调在一起消费订阅主题的所有分区。当然,每个分区只能由同一个消费者组内的一个Consumer实列来消费。
ConsumerGroup之间批次独立,互不影响,他们能订阅相同的主题而互不干涉。kafka仅仅使用consumer Group这一种机制,却实现了传统消息引擎系统的俩大模型:如果所有实列属于同一个Group,那么它实现的就是消息队列模型;如果所有实列分别属于不同的Group,那么他实现的就是发布/订阅模型。
理想情况下,Group中的Consumer实列的数量应该等于该Group订阅主题的总分区数。
ConsumerGroup的位移也是提交到Broker中的内部主题中的_consumer_offsets
ConsumerGroup中的consumer实列数在理想情况下是等于订阅主题的分区总数的。
(Group中的分区只能被一个消费者消费,一个消费者可以消费多个分区)
有三种情况:
1.消费者实列数小于分区数:这种情况会下,一个消费者实列会消费多个分区
2.消费者实列数等于分区数: 一个消费者消费一个分区
3.消费者实列数大于分区数:会有几个消费者处于空闲的状态下。
从平衡:Rebalance
Rebalance 本质上是一种协议,规定了一个一个ComsumerGroup下的所有Consumer如何达成一致,来分配订阅主题的分区。比如某个Group下有20个Consumer实列,他订阅一个具有100个分区的Topic。正常情况下,kafka会平均为每个Consumer分配5个分区。这个分配的过程就叫做Rebalance。
那么时候回会触发Rebalance?一下三种情况下回触发:
- Group中消费者实列数量发生变化。比如有新的Consumer加入Group,或者有Consumer实列奔溃退出Group
- 订阅的主题数发生变更。ComsumerGroup可以使用正则表达式的方式订阅主题。
- 订阅主题的分区数发生变更。kafka当前只能允许增加一个主题分区数。当分区数增加时,就会触发订阅该主题的所有Group发生Rebalance。
Kafka 新版本 consumer 默认提供 了 3 种分配策略,分别是 range 策略、round-robin 策略和 sticky 策略。
所 谓 的 分 配 策 略 决 定 了 订 阅 topic 的 每 个 分 区 会 被 分 配 给 哪 个 consumer。
range 策略主要是基于范围的思想。它将单个 topic 的所有分区 按照顺序排列,然后把这些分区划分成固定⼤⼩的分区段并依次分配给每个 consumer;
round-robin 策略则会把所有 topic 的所有分区顺序摆开,然后 轮询式地分配给各个 consumer。
最新发布的 sticky 策略有效地避免了上述 两种策略完全⽆视历史分配⽅案的缺陷,采⽤了「有黏性」的策略对所有 consumer 实例进⾏分配, 可以规避极端情况下的数据倾斜并且在两次 rebalance 间最⼤限度地维持了之前的分配⽅案。
通常意义上认为, 如果 group 下所有 consumer 实例的订阅是相同, 那么使⽤ round-robin 会带来更公平的分配⽅案,否则使⽤ range 策略的效 果更好。此外,sticky 策略在 0.11.0.0 版本才被引⼊,故⽬前使⽤的⽤户并 不多。新版本 consumer 默认的分配策略是 range。⽤户根据 consumer 参 数 partition.assignment.strategy 来进⾏设置。另外 Kafka ⽀持⾃定义的分 配策略,⽤户可以创建⾃⼰的 consumer 分配器(assignor)。 针对 rebalance 过程中的分区分配,下⾯举⼀个简单的例⼦,加以说 明。假设⽬前某个 consumer group 下有两个 consumer:A 和 B。当第 3 个 成员 C 加⼊时,满⾜了前⾯谈到的第⼀个触发条件,因此 coordinator 会执 ⾏ rebalance,并根据 range 分配策略重新为 A、B 和 C 分配分区, 如图 5.7 所示。 由此可⻅,原先 A 和 B 分别处理 3 个分区的数据,rebalance 之后 A、 B 和 C 各⾃承担 2 个分区的消费, 可以说这个分配⽅案⾮常公平, 每个 consumer 上的负载是相同的。