@
深入客户端
1. 分区分配策略
设置消费者与订阅主题之间的分区分配策略。
1.1 RangeAssignor分配策略
RangeAssignor 分配策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
==通俗地讲: m = 分区数 / 消费者 n = 分区数 % 消费者 ==
==前 n 个消费者 分配 m + 1个分区
后 面的消费者 分配 m 个分区 ==
这样分配的缺点?
但是这样不配并不均匀,如果只有3个分区,2个消费者,那么第一个消费者会一直分配2个分区,这样的现象如果扩大,使得部分消费者过载。
1.2 RoundRobinAssignor分配策略
分配策略的原理是将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序,然后通过轮询方式逐个将分区依次分配给每个消费者。
1.3 StickyAssignor分配策略
(1)分区的分配要尽可能均匀。
(2)分区的分配尽可能与上次分配的保持相同。
1.4 也可以进行自定义分配策略
可以通过自定义分区分配策略来打破Kafka中“一个分区只能被同一个消费组内的一个消费者消费”的禁忌。
有一个严重的问题—默认的消费位移的提交会失效。所有的消费者都会提交它自身的消费位移到__consumer_offsets中,后提交的消费位移会覆盖前面提交的消费位移。
应用价值不大。
2. 消费者协调器和组协调器
如果消费者客户端中配置了两个分配策略,那么以哪个为准呢?
如果有多个消费者,彼此所配置的分配策略并不完全相同,那么以哪个为准?
多个消费者之间的分区分配是需要协同的,那么这个协同的过程又是怎样的呢?
这一切都是交由消费者协调器(ConsumerCoordinator)和组协调器(GroupCoordinator)来完成的
2.1 旧版消费者客户端存在的问题
==旧版的kafka并没有组协调器 和 消费协调器 ==,是通过zookeeper 的监听器(Watcher)来实现的。
每个消费组(<group>)在ZooKeeper中都维护了一个/consumers/<group>/ids路径,在此路径下使用临时节点
每个消费者在启动时都会在/consumers/<group>/ids和/brokers/ids 路径上注册一个监听器。
当/consumers/<group>/ids路径下的子节点发生变化时,表示消费组中的消费者发生了变化;
当/brokers/ids路径下的子节点发生变化时,表示broker出现了增减。
这样通过ZooKeeper所提供的Watcher,每个消费者就可以监听消费组和Kafka集群的状态了。
这样带来的问题?
(1)羊群效应(Herd Effect):所谓的羊群效应是指ZooKeeper中一个被监听的节点变化,大量的 Watcher 通知被发送到客户端,导致在通知期间的其他操作延迟,也有可能发生类似死锁的情况。
(2)脑裂问题(Split Brain):消费者进行再均衡操作时每个消费者都与ZooKeeper进行通信以判断消费者或broker变化的情况,由于ZooKeeper本身的特性,可能导致在同一时刻各个消费者获取的状态不一致,这样会导致异常问题发生。
2.2 新版客户端再均衡原理
全部消费组分成多个子集,每个消费组的子集在服务端对应一个GroupCoordinator对其进行管理,GroupCoordinator是Kafka服务端中用于管理消费组的组件。
ConsumerCoordinator与GroupCoordinator之间最重要的职责就是负责执行消费者再均衡的操作,包括前面提及的分区分配的工作也是在再均衡期间完成的。
什么情况会触发再均衡(Reblance)?
- 有新的消费者加入消费组。
- 有消费者宕机下线。消费者并不一定需要真正下线,例如遇到长时间的 GC、网络延迟导致消费者长时间未向GroupCoordinator发送心跳等情况时,GroupCoordinator会认为消费者已经下线
- 有消费者主动退出消费组
- 消费组所对应的GroupCoorinator节点发生了变更
- 消费组内所订阅的任一主题或者主题的分区数量发生变化。