【1】消费者组、leader和follower
消费者想要拉取主题分区的数据,首先必须要加入到一个组中。
但是一个组中有多个消费者的话,那么每一个消费者该如何消费呢,是不是像图中一样的消费策略呢?如果是的话,那假设消费者组中只有2个消费者或有4个消费者,和分区的数量不匹配,怎么办?
所以这里,我们需要学习Kafka中基本的消费者组中的消费者和分区之间的分配规则:
- 同一个消费者组的消费者都订阅同一个主题,所以消费者组中的多个消费者可以共同消费一个主题中的所有数据。
- 为了避免数据被重复消费,所以主题一个分区的数据只能被组中的一个消费者消费,也就是说不能两个消费者同时消费一个分区的数据。但是反过来,一个消费者是可以消费多个分区数据的。
- 消费者组中的消费者数量最好不要超出主题分区的数据,就会导致多出的消费者是无法消费数据的,造成了资源的浪费。
消费者中的每个消费者到底消费哪一个主题分区,这个分配策略其实是由消费者的Leader决定的,这个Leader我们称之为群主。群主是多个消费者中,第一个加入组中的消费者
,其他消费者我们称之为Follower,称呼上有点类似与分区的Leader和Follower。
当消费者加入群组的时候,会发送一个JoinGroup请求。群主负责给每一个消费者分配分区。每个消费者只知道自己的分配信息,只有群主知道群组内所有消费者的分配信息。
【2】leader指定分配策略的基本流程
(1) 假设第一个消费者设定group.id为test,向当前负载最小的节点发送请求查找消费调度器
(2) 找到消费调度器后,消费者向调度器节点发出JOIN_GROUP请求,加入消费者组
(3) 当前消费者当选为群主后,根据消费者配置中分配策略设计分区分配方案,并将分配好的方案告知调度器
(4) 此时第二个消费者设定group.id为test,申请加入消费者组
(5) 加入成功后,kafka将消费者组状态切换到准备rebalance
,关闭和消费者的所有链接,等待它们重新加入。客户端重新申请加入,kafka从消费者组中挑选一个作为leader,其它的作为follower
。(步骤和之前相同,我们假设还是之前的消费者为Leader)
(6) Leader会按照分配策略对分区进行重分配,并将方案发送给调度器,由调度器通知所有的成员新的分配方案。组成员会按照新的方案重新消费数据
【3】四种分配策略
① RoundRobinAssignor(轮询分配策略)
每个消费者组中的消费者都会含有一个自动生产的UUID作为memberid。
轮询策略中会将每个消费者按照memberid进行排序,所有member消费的主题分区根据主题名称进行排序。
将主题分区轮询分配给对应的订阅用户,注意未订阅当前轮询主题的消费者会跳过。
从图中可以看出,轮询分配策略是存在缺点的,并不是那么的均衡,如果test1-2分区能够分配给消费者ccc是不是就完美了。
② RangeAssignor(范围分配策略)
按照每个topic的partition数计算出每个消费者应该分配的分区数量,然后分配,分配的原则就是一个主题的分区尽可能的平均分,如果不能平均分,那就按顺序向前补齐即可。
#所谓按顺序向前补齐就是:
假设【1,2,3,4,5】5个分区分给2个消费者:
5 / 2 = 2, 5 % 2 = 1 => 剩余的一个补在第一个中[2+1][2] => 结果为[1,2,3][4,5]假设【1,2,3,4,5】5个分区分到3个消费者:
5 / 3 = 1, 5 % 3 = 2 => 剩余的两个补在第一个和第二个中[1+1][1+1][1] => 结果为[1,2][3,4][5]
缺点: Range分配策略针对单个Topic的情况下显得比较均衡,但是假如Topic多的话, member排序靠前的可能会比member排序靠后的负载多很多。
还有就是如果新增或移除消费者成员,那么会导致每个消费者都需要去建立新的分区节点的连接,更新本地的分区缓存,效率比较低。
③ StickyAssignor(粘性分区)
在第一次分配后,每个组成员都保留分配给自己的分区信息。如果有消费者加入或退出,那么在进行分区再分配时(一般情况下,消费者退出45s后,才会进行再分配,因为需要考虑可能又恢复的情况),尽可能保证消费者原有的分区不变,重新对加入或退出消费者的分区进行分配。
从图中可以看出,粘性分区分配策略分配的会更加均匀和高效一些。
④ CooperativeStickyAssignor
前面的三种分配策略再进行重分配时使用的是EAGER
协议,会让当前的所有消费者放弃当前分区,关闭连接,资源清理,重新加入组和等待分配策略。明显效率是比较低的,所以从Kafka2.4版本开始,在粘性分配策略的基础上,优化了重分配的过程,使用的是COOPERATIVE
协议。
CooperativeStickyAssignor
是 Apache Kafka 中用于消费者组分区分配的一种策略。它旨在解决传统的轮询(RoundRobin)和粘性(Sticky)分配策略中的一些问题,特别是当消费者组中的消费者动态变化时,能够更有效地保持数据的局部性和均衡性。
什么是 CooperativeStickyAssignor?
CooperativeStickyAssignor
是一种改进的粘性分配策略,它试图在保持消费者组内部的分区分配尽可能稳定的同时,也能应对消费者组成员的变化。这种分配策略的目标是在消费者组成员动态变化的情况下,最小化重新分配对现有消费者的影响。
主要特点
-
粘性分配:
- 最小化重新分配:当消费者组中的消费者数量发生变化时,尽量减少重新分配的次数,以保持数据处理的连续性和一致性。
- 保持局部性:尽量让每个消费者保留其已经处理过的分区,这样可以减少数据的重新加载和处理开销。
-
合作分配:
- 动态调整:在消费者组成员变化时,能够动态调整分区分配,使得新增加的消费者能够平滑地加入到处理过程中。
- 均衡负载:确保每个消费者都能获得均衡的负载,避免过载或资源浪费的情况。
如何工作
CooperativeStickyAssignor
工作的基本原理如下:
-
初始化分配:
- 当消费者组首次启动时,分配策略会根据消费者组中的消费者数量和主题的分区数量来分配分区。
-
动态调整:
- 当有新的消费者加入时,
CooperativeStickyAssignor
会尝试将新加入的消费者分配到负载较少的消费者所拥有的分区中,同时尽量保持原有消费者的分区不变。 - 当有消费者离开时,
CooperativeStickyAssignor
会重新分配离开消费者所拥有的分区,尽量将这些分区分配给其他负载较低的消费者。
- 当有新的消费者加入时,
-
均衡负载:
- 在每次重新分配时,分配器都会考虑消费者的负载情况,确保每个消费者承担的分区数量大致相等。
CooperativeStickyAssignor
是一种先进的分区分配策略,它在保持分区分配的稳定性和局部性的同时,也能有效应对消费者组成员的变化。通过使用这种策略,可以提高消费者组处理数据的效率和稳定性。
【5】不同版本默认分配策略
Kafka 0.10.x 及之前版本
在 Kafka 0.10.x 及之前的版本中,默认的分区分配策略是 范围分配策略(Range Assignor)。这种策略会根据消费者的加入顺序来分配分区,每个消费者会获得一定范围内的分区。
Kafka 0.11.x 及之后版本
从 Kafka 0.11.x 版本开始,默认的分区分配策略是 粘性分配策略(Sticky Assignor)。粘性分配策略旨在保持消费者组中分区分配的稳定性,即在消费者组中的消费者数量发生变化时,尽量保持分区分配的一致性,减少重新分配的频率,从而降低数据处理的开销。
然而,值得注意的是,从 Kafka 2.8 版本开始,引入了 合作粘性分配策略(Cooperative Sticky Assignor),这是一种改进版的粘性分配策略。虽然默认分配策略仍然是 Sticky Assignor,但 CooperativeStickyAssignor 作为一种更高级的分配策略,已经在很多场景中被推荐使用。
分配策略的比较
-
Range Assignor:
- 在 Kafka 0.10.x 及之前版本中,默认使用 Range Assignor。
- 这种策略按照消费者的加入顺序分配分区,每个消费者会获得一个连续的分区范围。
- 缺点是当消费者组中的消费者数量发生变化时,可能会导致较大的重新分配,影响性能。
-
Sticky Assignor:
- 从 Kafka 0.11.x 开始,默认使用 Sticky Assignor。
- 目标是在消费者组中的消费者数量发生变化时,尽量保持分区分配的一致性,减少重新分配。
- 适用于需要保持分区分配稳定性的场景。
-
CooperativeStickyAssignor:
- 从 Kafka 2.8 版本开始引入。
- 这种策略进一步优化了 Sticky Assignor,使得在消费者组中的消费者数量发生变化时,能够更平滑地进行分区重新分配。
- 特别适用于需要动态调整消费者数量的场景。
总结
- 在 Kafka 0.10.x 及之前的版本中,默认的分区分配策略是 Range Assignor。
- 在 Kafka 0.11.x 及之后的版本中,默认的分区分配策略是 Sticky Assignor。
- 从 Kafka 2.8 版本开始,引入了 CooperativeStickyAssignor,这是一种更高级的粘性分配策略,虽然不是默认的分配策略,但在很多场景中被推荐使用。
通过合理的分区分配策略选择,可以优化消费者组的性能和稳定性。