【Kafka】【05】消费者 & Rebalance机制

1.消费方式

对于consumer而言,有两种消费方式:
(1)consumer主动向broker拉取数据 push
(2)broker主动向consumer推送数据 pull
kafka中,consumer采用主动从broker中拉取数据

  • push模式下消息发送速率是由broker决定的,它的目标是尽可能以最快速度传递消息,很难适应消费速率不同的消费者;这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。

  • pull模式则可以根据consumer的消费能力以适当的速率消费消息。pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即为timeout

2.消费者分区分配策略

分区分配策略要解决的问题:
     一个consumer group中有多个consumer,一个 topic有多个partition,所以必然会涉及到partition的分配问题,即确定哪个partition由哪个consumer来消费


  • 订阅topic不是以group为单位的,而是以消费者为单位;
    一个group的多个consumer之间可以订阅不同的topic;

参考资料
在这里插入图片描述


kafka三种分区分配策略:
Kafka有三种分配策略,RoundRobin,Range , Sticky

在 Kafka内部存在两种默认的分区分配策略:Range和 RoundRobin


1)Range

  • Range是默认策略。
  • Range是对每个Topic而言的(即以topic为单位,一个Topic一个Topic分,各个topic之间分配时没有任何关联)
  • 分配过程:
    首先对同一个Topic里面的分区按照序号进行排序,并对消费者线程数按照字母顺序进行排序。然后用Partitions分区的个数除以消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。

例如
假设我们有两个topic : T1 和 T2;T1有10个分区0~9; T2有10个分区0~9
1个消费者组G,有两个消费者(C1,C2);3个消费者线程 C1-0、C2-0、C2-1;

分配过程如下:

(1)每个topic单独分配
分配T1:
   T1  的十个分区0~9 按顺序展开
   消费者线程按字母顺序展开
   10/3 = 3 ...1
		 C1-0 将消费 0, 1, 2, 3 分区
	     C2-0 将消费 4, 5, 6 分区
	     C2-1 将消费 7, 8, 9 分区

以同样的过程分配T2

分配特点:消费者线程之间所消费的分区数差异最多为1

topic分区数:Tn
消费者线程个数:Cn
Tn/Cn = n ... m 
m必然小于Cn大于等于0,前m个消费者线程消费n+1个partition,后面的就消费n个

源码注释:

The range assignor works on a per-topic basis. For each topic, we lay out the available partitions in numeric order and the consumers in lexicographic order. We then divide the number of partitions by the total number of consumers to determine the number of partitions to assign to each consumer. If it does not evenly divide, then the first few consumers will have one extra partition.
For example, suppose there are two consumers C0 and C1, two topics t0 and t1, and each topic has 3 partitions, resulting in partitions t0p0, t0p1, t0p2, t1p0, t1p1, and t1p2.
The assignment will be:
C0: [t0p0, t0p1, t1p0, t1p1]
C1: [t0p2, t1p2]

2)roundrobin

roundrobin策略针对于全局所有的topic和消费者,分配步骤如下:

  1. 消费者按照字典排序,例如C0, C1, C2… …,并构造环形迭代器。
  2. topic名称按照字典排序,并得到每个topic的所有分区,从而得到所有分区集合。
  3. 遍历第2步所有分区集合,同时轮询消费者。
  4. 如果轮询到的消费者订阅的topic不包括当前遍历的分区所属topic,则跳过;否则分配给当前消费者,并继续第3步。

例如:只有1个topic的情况
如果有5个分区(P0, P1, P2, P3, P4),且订阅这个topic的消费者组有2个消费者(C0, C1)。那么P0, P2, P4将被C0消费,P1, P3将被C1消费。

例如:多个topic
3个Topic:T0(3个分区0, 1, 2), T1(两个分区0, 1), T2(4个分区0, 1, 2, 3);
3个consumer: C0订阅了[T0, T1], C1订阅了[T1, T2], C2订阅了[T2, T0];

roundrobin结果分配结果如下:
T0-P0分配给C0,T0-P1分配给C2,T0-P2分配给C0,
T1-P0分配给C1,T1-P1分配给C0,
T2-P0分配给C1,T2-P1分配给C2,T2-P2分配给C1,T2-P3分配给C2;

多个topic例2

  1. 消费者字典排序且构造成环形队列[C0, C1, C2];C0订阅了[t0],C1订阅了[t0, t1],C2订阅了[t0, t1, t2];
  2. topic字段排序即[t0, t1, t2],t0只有一个分区p0,t1有两个分区p0和p1,t2有三个分区p0,p1和p2。得到这三个topic下所有分区集合[t0p0, t1p0, t1p1, t2p0, t2p1, t2p2];
  3. 开始遍历所有分区。
  4. 遍历分区t0p0,同时消费者为C0,C0订阅了t0这个topic,所以分区t0p0分配给C0这个消费者;
  5. 遍历分区t1p0,同时消费者为C1(每次消费者都需要轮询),C1订阅了t1,所以分区t1p0分配给C1这个消费者;
  6. 遍历分区t1p1,同时消费者为C2,C2订阅了t1这个topic,所以分区t1p1分配给C1这个消费者;
  7. 遍历分区t2p0,同时消费者为C0,C0没有订阅t1,轮询到消费者C1,C1也没有订阅t2,轮询到C2,C2订阅了t2这个topic,所以分区t2p0分配给C2这个消费者;
  8. 遍历分区t2p1,同时消费者为C0,C0没有订阅t1,轮询到消费者C1,C1也没有订阅t2,轮询到C2,C2订阅了t2这个topic,所以分区t2p0分配给C2这个消费者;
  9. 遍历分区t2p2,同时消费者为C0,C0没有订阅t1,轮询到消费者C1,C1也没有订阅t2,轮询到C2,C2订阅了t2这个topic,所以分区t2p0分配给C2这个消费者;
  10. 遍历完所有分区,over。

源码注释:
The round robin assignor lays out all the available partitions and all the available consumers. It then proceeds to do a round robin assignment from partition to consumer. If the subscriptions of all consumer instances are identical, then the partitions will be uniformly distributed. (i.e., the partition ownership counts will be within a delta of exactly one across all consumers.) For example, suppose there are two consumers C0 and C1, two topics t0 and t1, and each topic has 3 partitions, resulting in partitions t0p0, t0p1, t0p2, t1p0, t1p1, and t1p2. The assignment will be:
C0: [t0p0, t0p2, t1p1]
C1: [t0p1, t1p0, t1p2]
When subscriptions differ across consumer instances, the assignment process still considers each consumer instance in round robin fashion but skips over an instance if it is not subscribed to the topic. Unlike the case when subscriptions are identical, this can result in imbalanced assignments. For example, we have three consumers C0, C1, C2, and three topics t0, t1, t2, with 1, 2, and 3 partitions, respectively. Therefore, the partitions are t0p0, t1p0, t1p1, t2p0, t2p1, t2p2. C0 is subscribed to t0; C1 is subscribed to t0, t1; and C2 is subscribed to t0, t1, t2. Tha assignment will be:
C0: [t0p0]
C1: [t1p0]
C2: [t1p1, t2p0, t2p1, t2p2]

注意:无论在哪种分配策略下:如果消费者组修改了订阅的topic 或者增加了 消费者组 都会导致重新分配!

3 offset的维护

3.1 消费者offset的作用

由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。

3.2 offset存放位置

  • Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中
  • 从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为__consumer_offsets。

3.3 消费offset案例

思路: __consumer_offsets 为kafka中的topic, 那就可以通过消费者进行消费.

方法步骤:
(1)修改配置文件consumer.properties

# 不排除内部的topic
exclude.internal.topics=false

(2)创建一个topic

bin/kafka-topics.sh --create --topic user1 --zookeeper hadoop102:2181 --partitions 2 \
--replication-factor 2

(3)启动生产者和消费者,分别往user1生产数据和消费数据

bin/kafka-console-producer.sh --topic user1 --broker-list  hadoop102:9092
bin/kafka-console-consumer.sh --consumer.config config/consumer.properties --topic user1 --bootstrap-server hadoop102:9092

(4)消费offset

bin/kafka-console-consumer.sh --topic __consumer_offsets \
--bootstrap-server hadoop102:9092  \
--formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" \
--consumer.config config/consumer.properties --from-beginning

(5)消费到的数据

[test-consumer-group,user1,1]::OffsetAndMetadata(offset=2, leaderEpoch=Optional[0],
 metadata=, commitTimestamp=1591935656078, expireTimestamp=None)
 
[test-consumer-group,user1,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional[0],
metadata=, commitTimestamp=1591935656078, expireTimestamp=None)
  • 可以看出:offset是以 group-topic-partition为单位进行维护的;
    哪个group消费的某个topic的哪个partition到哪个位置
  • offset的值 是下一个待消费的消息的offset

4.Rebalance机制

参考博客:
线上Kafka突发rebalance异常,如何快速解决?

1)消费者组的五个状态

1.Kafka 为消费者组定义了 5 种状态:
在这里插入图片描述
(1)Dead:组内没有消费者 且 Cordinator中没有该group的元数据信息
(2)Empty: 组内没有消费者 但是Cordinator中还存有该group的消费offset记录
(3)PreparingRebalance: 消费者组Rebalance准备阶段,group所有成员需要重新发起加入group的请求
(4)CompletingRebalance: group中所有consumer已经加入,等待分区分配
(5)Stable: group中各个consumer已经分配完毕,能够正常消费数据了
2.状态转换图
在这里插入图片描述

2)Rebalance 完整流程

  • Rebalance的完整流程需要消费者端和协调者组件共同参与才能完成
  • Rebalance 过程对 consumer group 会造成比较严重的影响。在 Rebalance 的过程中 consumer group 下的所有消费者实例都会停止工作,等待 Rebalance 过程完成。

Rebalance完整流程:
重平衡分为两个步骤:

  • 加入组
  • 等待领导消费者(Leader Consumer)分配方案。
    这两个步骤分别对应两类特定的请求:JoinGroup 请求和 SyncGroup 请求。

(1)JoinGroup请求
在这里插入图片描述

step.1 group 中 consumer会向协调者发送 JoinGroup 请求,请求加入group;(Ps:JoinGroup请求中包含了consumer订阅的topic信息)
step.2 当coordinator收集了全部成员的 JoinGroup 请求后,coordinator会从这些consumer中选择一个担任这个group的consumer leader,并把组成员信息以及订阅信息封装进JoinGroup响应发给leader

PS: 
	通常情况下,第一个发送 JoinGroup 请求的成员自动成为领导者
	图中,coordinator发送给成员1的JoinGroup响应中就封装了组成员信息及定于信息

step.3 consumer leader 负责消费分配方案的制定。进入到下一步:发送 SyncGroup 请求。

(2)SyncGroup请求
在这里插入图片描述
step.1 consumer leader 将刚刚做出的分配方案封装进发送 SyncGroup 请求发给coordinator

普通的 consumer也会向coordinator发送SyncGroup请求,但是没有实际内容

step.2coordinator接收到分配方案之后会把方案封装进SyncGroup响应发给各个consumer。这样组内的所有成员就都知道自己应该消费哪些分区了。

3)Rebalance 的3个触发条件

1)组成员个数发生变化。例如有新的 consumer 实例加入该消费组或者离开组。
2)订阅的 Topic 个数发生变化。
3)订阅 Topic 的分区数发生变化。

Rebalance就是重新进行 partition 的分配,从而使得消费的效率达到最高;

消费者组内成员发生变化的三种情况

1.新成员加入
在这里插入图片描述
当协调者收到新的 JoinGroup 请求后,它会通过心跳请求响应的方式通知组内现有的所有成员,强制它们开启新一轮的重平衡。
2.组成员主动离开
主动离开就是指消费者实例所在线程或进程调用 close() 方法主动通知协调者它要退出。
这个场景就涉及到了第三类请求:LeaveGroup 请求。协调者收到 LeaveGroup 请求后,依然会以心跳响应的方式通知其他成员
在这里插入图片描述
3.组成员崩溃(被动离开)
崩溃离组是指消费者实例出现严重故障,突然宕机导致的离组。它和主动离组是有区别的,因为后者是主动发起的离组,协调者能马上感知并处理。但崩溃离组是被动的,协调者通常需要等待一段时间才能感知到,这段时间一般是由消费者端参数 session.timeout.ms 控制的。
在这里插入图片描述

Rebalance导致consumer提交offset

正常情况下,每个组内成员都会定期汇报位移给协调者。当重平衡开启时,协调者会给予成员一段缓冲时间,要求每个成员必须在这段时间内快速地上报自己的位移信息,然后再开启正常的 JoinGroup/SyncGroup 请求发送。
在这里插入图片描述

4)Rebalance解决方案

解决rebalance一般都是组员崩溃这种不可预测性的状况导致的;所以解决rebalance问题的场景就是组员长时间没有心跳响应连接超时导致的

kafaka 消费者相关的四个参数配置:
1.session.timeout.ms
consumer向broker发送心跳的最大时间间隔,超时broker判定此consumer死亡,会启动 rebalance。
2.heartbeat.interval.ms
心跳时间间隔,一般来说,session.timeout.ms 的值是 heartbeat.interval.ms 值的 3 倍以上。在一个超时周期内就可以有多次心跳,避免网络问题导致偶发失败。
3.max.poll.interval.ms
表示 consumer 每次 poll 消息的时长;如果当前批次消费时长超过此值,认为consumer死了,消费不动了,触发rebalance
4.max.poll.records
每次消费的消息数,获取的消息条数越多,需要处理的时间越长。
需要保证在 max.poll.interval.ms 设置的时间内能消费完,否则会发生 rebalance。

1)消费者心跳超时,导致 rebalance。

这个问题往往是网络不稳定导致的;
解决方法:session.timeout.ms设置大点,并且超时时间范围内设置多次心跳(调大session,调小heartbeat)

阿里云官方文档建议超时时间(session.timeout.ms)设置成 25s,最长不超过 30s。那么心跳间隔时间(heartbeat.interval.ms)就不超过 10s。

2)消费者处理时间过长,导致 rebalance

 一般是增加消费者处理的时间(max.poll.interval.ms),减少每次处理的消息数(max.poll.records)。

 阿里云官方文档建议 max.poll.records 参数要远小于当前消费组的消费能力
 (records < 单个线程每秒消费的条数 x 消费线程的个数 x session.timeout的秒数)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值