消息队列通常有两种消息获取模式:
Kafka采用 pull(拉)模式!!!
基本设计:
消费流程如下:
消费者端在向kafka集群抓取消息,会有几个初始化配置:
消费者组里的消费者可能分布在不同的主机上,与Kafka集群进行交互时也要涉及不同的主机,那么消费者之间是怎么分布式之间的协调的呢?这里就涉及到一个 coordinator 协调者概念
首先每一个broker中都会存在一个coordinator,消费者组借助其中一个coordinator辅助实现消费者组的初始化和分区的分配。选择策略是 根据groupId的hashcode值与offsets的分区数量求模 (如XXX主题的默认分区数是50,groupid的hashcode值 = 1,1% 50 = 1,那么XXX主题的1号分区,在哪个broker上,就选择这个节点的coordinator作为协调者)
选择出coordinator后,进行消费者组的初始化:
Kafka有3种主流的分区分配策略:
可以通过配置参数 partition.assignment.strategy,修改分区的分配策略,默认策略是Range + CooperativeSticky,Kafka可以同时使用多个分区分配策略。
再平衡策略:
0 号消费者挂掉之后,0 号消费者的任务会整体被分配到 1 号消费者或者 2 号消费者。
1 号消费者:消费到 3、4、0、1、2号分区数据。
2 号消费者:消费到 5、6 号分区数据。
数据倾斜问题:在上述进行分区分配时,只是针对 1 个 topic 而言,C0消费者多消费1个分区影响不是很大。但是如果有 N 多个 topic,那么针对每个 topic,消费者 C0都将多消费 1 个分区,topic越多,C0消费的分区会比其他消费者明显多消费 N 个分区。容易产生数据倾斜!
再平衡策略:
某个消费者挂掉之后,所有的分区会重新进行一次再分配!(可能会造成资源的浪费)
Sticky 分区策略主要有两个目的:
两者发生冲突时,第一个目标优于第二个目标
对于常规情况,Sticky 分区策略与RoundRobin 分区策略的分配结果基本一致
而对于某个消费者没有订阅消费者组内的某个主题的情况,Sticky 分区策略分配更加均衡,更加合理
再平衡策略:
0 号消费者挂掉之后,0 号消费者的分区以轮询的方式尽可能均匀地分配到1号消费者或者 2 号消费者。
而1号消费者或者 2 号消费者依旧保持原有的分区,即重分配后尽可能和上次分配保持相同,使分配策略具备一定“黏性”,从而减少系统资源的消耗和异常情况的发生
这里还存在一个问题,如果有多个消费者,彼此配置的分配策略并不完全相同,那么需要以哪个为准?
这就涉及到了再平衡的原理:全部消费者组被分为多个子集,每个消费者组子集在服务端都对应一个 GroupCoordinator 对其进行管理。而 GroupCoordinator 最重要的职责就是负责执行消费者在均衡的操作
再平衡的具体步骤涉及一系列步骤,这里不展开,而对于上面的不同分配策略的问题,执行策略为:
从0.9版本开始,consumer默认将offset保存在broker的partition中。上文讲到了 coordinator 协调者这个概念,coordinator 选择完毕后,消费者组下的所有的消费者提交offset的时候就会往coordinator 所在的这个分区去提交offset
offset的提交方式:
不同点是同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。
指定Offset 消费策略:
当 Kafka 中没有初始偏移量(如消费者第一次消费,宕机,数据被删除),可以指定从哪个 offset 开始消费:
auto.offset.reset = earliest | latest | none
自动提交和手动提交可能会导致消息的重复消费和漏消费,如果要确保每个消息有且仅被消息一次,需要借助事务消息!
参考文献:https://zhuanlan.zhihu.com/p/371361083
https://blog.csdn.net/wanger61?spm=1000.2115.3001.5343