Kafka技术内幕 读书笔记之(五) 协调者——消费组状态机


  协调者保存的消费组元数据中记录了消费组的状态机 , 消费组状态机的转换主要发生在“加入组请求”和“同步组请求”的处理过程中 
协调者处理“离开消费组请求”“迁移消费组请求”“心跳请求” “提交偏移量请求”也会更新消费组的状态、机,或者依赖消费组的状态进行不同的
处理。消费者要入消费组 , 需要依次发送“入组请求”和“同步组请求”给协调者 。 消费者加入组的过程作再平衡,因为协调者处理这两
种请求会更新消费组状态,所以再平衡操作跟消费组状态也息息相关。 

监听器回调方法和再平衡操作的执行顺序如下
(1)消费者准备入组,调用“消费者再平衡监听器”的 onPartitionsRevoked ()方法
(2)消费者发送“入组请求 调者开始处理消费者的“加入组请求”,执行再平衡操作
(3消费者完成入组,调用“消费者再平衡监昕器”的 on PartitionsAssigned ()方法

1. 3个消费者,一次再平衡操作
  3个消费者同时发送了“加入组请求” 因为在“消费组元数据上”加了锁的保护,只能一次处理一个消费者的“入组请求"。个消费者收到
加入组响应之前,第个消费者的加入组请求不会被处理该场景下共只发生了次再平衡操作, 只执行了一次分区分配工作, 3个消费者最
后都收到了分配的分区 只执行一次再平衡操作,并不意味着消费组的状态为“准备再平衡”只有一次 : 第一个消费者加入组,消费组状态
从“稳定”到“准备再平衡”再到“等待同步”;第个消费者加入组时,消费组状态会从“等待同步”到“准备再平衡气 

下面是再平衡操作时Kafka服务端相关的日志记录,日志对应的请求发送和处理步骤如下
(1) 3个消费者同发送入组请求, consumer- 3 (第一个消费者)的请求优先被协调者处理
(2) consumer- 3 (主消费者)完成延迟操作 消费组状态改为“等待同步”。
(3) 处理consumer- 2 的“入组请求,消费组状态改为“准备再平衡”,延迟操作不能完成
(4) 处理consumer- 1 的“加入组请求”,延迟操作不能完成;处理consumer - 3的同步请求。
(5) 消费组状态为“再平衡”, consumer- 3重新发送“加入组请求”
(6) 延迟操作可完成,返入组响应”给3个消费者 ; consumer - 3发送“同步组请求”
(7) 协调者保存 consumer 3发送的分配结果时,也会处理普通消费者的“同步组请求”
(8) 消费组的分配结果保存完毕后,返回“同步组响应”给所有的消费者,消费组状态为“稳定”
(9) 关闭应用程序,消费组状态为“离开”, 3个消费者都会从消费组中移除

种场景“3个消费者同时发送加入组请求”有以下几个特点
- 每个消费者再平衡监听器的 onPartitionsRevoked  ()方法和onPartitionsAssigned ()方法都只调用。 这说明3个消费者加入消费组,
 总共只发生次再平衡操作,并且只分配次分区
- 第个消费者和第个消费者都会调用 prepareRebalance ()方法,它们都将消费组状态改为“准备再平衡” 。 个消费者更改前的状态
  是“稳定”,第二个消费者更改前的状态是“等待同步” 
- 在这次再平衡操作中,消费组的状态变依次是 :稳定准备再平衡等待同步准备再平衡等待同步稳定 。 最后关闭应用程序,消费组状态为“离开” 
- 个消费者发送了两次“加入组请求”,第个和第个消费者各向发送了次“加入组请求” 。 

2. 3个消费者,两次再平衡操作
  第二种场景: 个消费者启动后,过秒启动第二、三个消费者 个消费者启动后,再过秒启动第个消费者。 这么做的目的是让第个消费者
收到“加入组响应”保证它发送“同步组请求” 、收到“同步组响应”之前,都不会有新消费者加入消费组(睡眠了秒,足够第个消费者来完成这些操作)

  第种场景中消费组状态还是“待同步” 个消费者还没发送“同步组请求” 个消费者发送“加入组请求”,会待第个消费者重新发送
“加入组请求” 种场下第个消费者发送了“同步组请求”,并收到“同步组响应”,更改消费组状态为“稳定” 。 在这之后,第个消费者
才发送了“加入组请求”,和第种场景样,同样要完成延迟操作,协调者会等待第个消费者重新发送“加入组请求” 

  第个消费者调用了两次onPartltionsRevoked ()和 onPartitionsAssigned ()方法,说明第个消费者与了两次再平衡操作 。
个消费者和第个消费者都只调用了次回调方法,说明它们只参与了次再平衡操作 。 次再平衡操作只有第个消费者,
所以分配结果是3个分区都分配给了第一个消费者 。 次再平衡操作有3个消费者参与,所以每个消费者都分配到了个分区 

  在第个和第个消费者调用onPartltionsRevoked()方法 ,协调者还没有开始处理它们的请求之前,即还没有开始第次再平衡操作,
个消费者因为在第次再平衡操作分配到了分区,所以它可以拉取到消息( Message_Z 。但是当协调者开始处理第个和第
 任何个消费者的请求时,开始第次再平衡操作,第个消费者就不能再拉取消息了 。 因为消费组状态被更新为“准备再平衡”,
个需要重新发送“加入组请求”才能与第次再平衡操作 种场景消费组的状态变化过程:
  稳定准备再平衡→等待同步→稳定→准备再平衡→等待同步→稳定
下面是第种场景的服务端日志,它和第种场景的区别是发生了两次再平衡操作,具体步骤如下
(1)协调者处理consumer- 1的加入组请求,并且完成第次再平衡,分配到分区
(2)协调者处理consumer- 2 的加入组请求,消费组状态为“准备再平衡”,延迟操作不能完成
(3)协调者处理consumer- 3 的加入组请求,延迟操作还不能完成
(4 ) consumer- 1重新发送加入组请求,延迟操作完成,返回“加入组响应”给所有消费者
(5 ) consumer- 1是主消费者,它发送“同步组请求,返回“同步组响应”给 consumer- 1
(6 ) consumer - 2和consumer - 3是普通消费者,协调者依次处理并返回“同步组响应”
(7)关闭应用程序,消费组状态为“离开”, 3个消费者都会从消费组中移除

3. 3个消费者, 3次再平衡操作
  第三种场景: 个消费者启动后,过秒启动第个消费者,再过两秒启动第个消费者 在第个消费者加入消费组后,设置两秒的睡眠时间,
是为了保证前面两个消费者完成第次再平衡操作之前,暂时不让第个消费者提前加入消费组。 这种场景相比第种又多了次再平衡操作,即每
个消费者加入消费组,都会触发次再平衡操作 个消费者经历了 3次再平衡,第个消费者经 历了两次再平衡,第个消费者经历了一次再平衡: 

从第种场景服务端的日志也可以看出,除去关闭应用程序, 3个消费者依次力加入消费组,总共发生3次再平衡操作 。消费组的状态变化过程:
 稳定→准备再平衡→等待同步稳定(第一次再平衡结束→准备再平衡→等待同步→稳定(第二次再平衡结束→准备再平衡等待同步→稳定(第三
次再平衡结束) 具体步骤如下
(1)协调者处理 consumer- 1 的加入组请求,并且完成第次再平衡,消费组状态为“稳定”
(2)协调者处理 consumer- 2 的加入组请求,消费组状态为“准备再平衡”,延迟操作不能完成
(3 ) consumer - 1重新发送加入组请求,延迟操作完成,返回“加入组响应”给两个消费者
(4)两个消费者都发送了“同步组请求”,协调者返回“同步组响应”给两个消费者,状态为“稳定”
(5)协调者处理consumer- 3 的加入组请求,延迟操作不能完成, 等待前两个消费者重新加入组
(6 )  consumer1和consumer- 2重新发送“加入组请求”,延迟操作完成,状态为“等待同步”
(7) 所有消费者都发送了“同步组请求”,协调者返回“同步组响应”给所有消费者,状态为“稳定”。
(8) 关闭应用程序,消费组状态为“离开”, 3个消费者都会从消费组中移除

要理解不同消费者按照不同顺序加入消费组后,协调者处理请求的执行顺序,需要理解下面这些概念
(1)协调者处理请求时,对消费组元数据加锁保护,保证不会同时处理多个请求
(2)消费组状态为“稳定”或“等待同步”,都可以转为“准备再平衡”,并创建延迟的操作
(3)消费组状态为“准备再平衡”时,主消费者发送“同步组请求”的分配结果是无效的
(4)如果消费组中有消费者,但它们还没有重新发送加入组请求,延迟操作不能完成
(5)旦消费组中所有消费者的回调方法不为空,延迟操作可以完成,并返回加入组响应

消费组的状态转换
  一次再平衡操作的正常消费组状态变化过程是:稳定→准备再平衡→等待同步→稳定,这个顺序是按顺时针转动的 。 

  每次再平衡操作,协调者都会创建个延迟的操作对象协调者处理多个消费者的请求,并不会创建多个延迟的操作对象
  协调者在处理“加入组请求”和“同步组请求”时,会对“消费组元数据”进行加锁。 协调者只有释放了锁,才可以处理其他消费者的请求 
协调者处理同个消费者的“加入组请求”和“同步组请求”,这两个操作的顺序是固定的,锁的持有永远不会冲突 。 但协调者处理不同消费
者的不同请求时,则有可能发生锁被占用的情况,此时需要等待锁释放后才可以执行比如协调者处理第一个消费者的“加入组请求”时,
就不能处理其他消费者的“加入组请求”;同样,协调者如果正在处理第个消费者的“同步组请求”,也不能处理其他消费者的“加入组请求”或者“同步组请求” 。

协调者处理“加入组请求”
  个消费者重新发送“加入组请求”之前,第三个消费者先发送了“加入组请求” 。 协调者处理第个消费者的人口状态是“准备再平衡”,但并不会
改变消费组的状态,这种情况比较特别,但也是允许的

  消费组状态设置为“准备再平衡”时(不管是从“稳定”状态进入,还从“待同步”状进入),会试完成次延迟的操作。这是因为协调者处理“加入组请求”时,
次都创建延迟的操作,所以不定每次都会调用tryCompleteElseWatch ()方法 。 因为与延迟操作相关的外部事件有可能会完成它,所以只要有
触发它的外部事件,就应该调用 checkAndComplete ()尽快完成延迟的操作 。 

  协调者处理消费者的“加入组请求”还要考虑消费者是否已经在消费组中 如果消费者以“未知编”发送 “加入组请求”,协调者先创建对应的消费者
成员元数据,然后将其入到消费组元数据中 。 在协调者返回给消费者第次的“入组应”结果中,要带有分配成员编号 , 这样下
消费者以分配的成员编号送“加入组请求”,协调者就可以从消费组元数据中找出对应编号的消费者成员元数据。 

   消费组到达“稳定”状态后,表示协调者处理了主消费者发送的“同步组请求”,但这并不意味着其消费者都发送了“同步组请求” 
如果普通消费者发送了“加入组请求”,但
没有收到“加入组响应”,需要重新发送“加入组请求” 协调者处理这种消费者重新发送的“加入组
请求”时,因为消费组状态已经是“稳定”,所以它会立即返回“加入组响应”给消费者 具体步骤如下

(1 ) 3个消费者发送“加入组请求”给协调者,消费组状态从“准备再平衡”到“等待同步”
(2)协调者返回“加入组响应”给 3个消费者,前两个消费者收到“加入组响应”,第个没收到响应 
(3)第个消费者是普通消费者,它发送“同步组请求”,协调者处理时只是设置对应的回调方法
(4)第一个消费者是主消费者,它完成分区分配,发送“同步组请求”给协调者,消费组状态改为“稳定” 
(5)协调者返回“同步组响应”给两个消费者,因为只有前两个消费者的回调方法不为空
(6)第个消费者没收到“加入组响应,它重新发送“加入组请求”。
(7)协调者处理第个消费者的“加入组请求”,消费组状态是“稳定”,立即返回“加入组响应”
(8)第个消费者因为不是主消费者,它收到“加入组响应”,会立即发送“同步组请求”
(9)协调者处理第个消费者的“同步组请求”,消费组状态是“稳定”,立即返回“同步组响应”。

上面的示例是普通消费者没有收到“加入组响应”,但如果是主消费者没有收到“加入组响应”就不同了 。 由于主消费者没有收到“加入组响应”,
它就不可能执行分区分配工作,也不会发送“同步组请求” 。 协调者也不会将消费组状态更改为“稳定”,因此仍然停留在“等待同步”,
只能等主消费者及时地重新发送“加入组请求” 这种场景下,整个过程的具体步骤如下

(1 ) 3个消费者发送“加入组请求”给协调者,消费组状态从“准备再平衡”到“等待同步”
(2)协调者返回“加入组响应”给 3个消费者,前两个消费者收到“加入组响应”。 
(3)前两个消费者都是普通消费者,它发送“同步组请求”,协调者处理时只是设置对应的回调方法 
(4) 主消费者没有收到“加入组响应”,重新发送“加入组请求”
(5)协调者处理主消费者的“加入组请求”,消费组状态是“等待同步”, 即返回“加入组响应”
(6)主消费者收到“加入组响应”,执行分区分配工作,并发送“同步组请求”
(7)协调者处理主消费者的“同步组请求”,返回响应给所有消费者,更新消费组状态为“稳定”


协调者处理“同步组请求”
  协调者处理消费者发送的“同步组请求”时,还会从“准备再平衡”“稳定”两种状态进入 先来看消费组状态为“准备再平衡”的处理步骤 。 
(1)第个消费者加入组,消费组状态为“等待同步”,第一个消费者同时作为主消费者还在执行分区分配作 
(2)第个消费者作为新的消费者加入组,将消费组状态改为“准备再平衡”
(3)第个消费者执行完分区分配工作,会发送“同步组请求”给协调者
(4)协调者处理第个消费者的“同步组请求”,由于消费组状态是“准备再平衡”,它会返回“正在再平衡”的错误码给第个消费者 
(5)第个消费者收到错误的“同步组响应”,会重新发送“加入组请求 ”。

再来看协调者处理“同步组请求”,从“稳定”状态进入场景。有三个消费者,第一个是主消费者,第二个和第个都是普通消费者 。 它们都加入组后,
消费组状态为“等待同步” 。 下面的步骤是协调者处理这3个消费者发送“同步组请求”的不同执行顺序和流程
(1)第二个消费者收到“加入组响应”后立即发送“同步组请求”,由于第个消费者还没发送“同步组请求”,协调者处理第二个消费者的“同步组请求”
  会设置第个消费者成员元数据的回调方法
(2)第一个消费者执行完分区分配,发送“同步组请求” 协调者处理第个消费者的“同步组请求”,也会设置第一个消费者成员元数据的回调方法
(3)协调者返回“同步组响应”给元数据中有回调方法的消费者,即第个消费者和l个消费者 。 因为第个消费者还没发送“同步组请求”,
  所以它的元数据回调方法为,协调者不会返回响应给它 
(4)协调者发送完“同步组响应”后,更改消费组状态为“稳定”
(5)第个消费者发送“同步组请求”,由于消费组状态为“稳定”,协调者直接返回“同步组响应”给它 

前面我们分析了协调者处理“加入组请求”和“同步组请求”的流程,以及相关的消费组状态机转换。 除此之外,消费者也可能会离开消费组。 比如 消费
者应用程序被手动停掉,虽然消费者不会马上从消费组元数据中移除,但是协调者会对注册在消费组中的所有消费者进行监控, 旦消费者没有反
应,就会将其从消费组中移除如果消费组中所有消费者成员都离开了,协调者也会把消费组删除掉

协调者处理“离开组请求”

  消费者离开消费组有多种情况 比如消费者应用程序被关闭,或者应用程序没有关闭,但消费者不订阅主题了 。 消费者离开消费组,表示协调者
不需要在消费组中管理这个消费者了 消费者客户端的工作如下 

(1)取消定时心跳任务,因为离开组意味着不被协调者管理,就不需要向协调者发送心跳了
(2)通过消费者的协调者对象( ConsumerCoordinator)发送“离开组请求”给协调者。
(3)重置相关的信息,比如设置成员编号为“未知编号”、重置 rejolnNeeded变革t为 false
  “离开组请求”的处理和“加入组请求”“同步组请求” 样,都会对“消费组元数据”进行加锁,而且都涉及消费组的状态机转换。 

第一种场景 消费组状态是 准备再平衡”。由于这个状态一定存在延迟的操作,而且还没到“等待同步”,说明延迟的操作还没有完成
那么消费者的离开,有可能会导致延迟的操作可以完成,所以需要通过延迟缓存检查是否能完成延迟的操作

第二种场景:消费在且状态是 稳定 或者 等待同步”。这两个状态说明要离开的消费者在这之前,已经收到“加入组响应”或者收到“同步组响应” 
 消费组状态为“等待同步”,说明延迟操作已经完成,消费者已经在消费组中 。 主消费者在分配分区时,为消费组所的消费者分配分区,当然
包括这个即将离开的消费者 现在消费者要离开,原本分配给它的分区应该重新分配给其消费者,所以需要执行再平衡操作
 
再平衡超时与会话超时

  协调者等待“延迟操作”完成有一个时间限制,它会选择消费组中所有消费者会话超时时间的最大值,作为“再平衡操作的超时时间”,也叫作“延迟
操作的超时时间” 如果是第个消费者加入组,再平衡操作的时间等于第一个消费者的会话超时时间 。 但因为目前消费组中只有第一个消费者,
所以协调者刚刚创建的延迟操作可以马上完成 。 当第二个消费者加入组后,再平衡操作的时间会选择两个消费者的会话超时时间最大值
比如第个消费者的会话超时时间是 10秒,第二个消费者的会话超时时间是5秒,再平衡操作的超时时间等于 10秒,延迟的加入操作会最多等待 10秒,
等待第个消费者在这段时间内可以重新发送“加入组请求” 

  为延迟操作设置超时时间是为了防止延迟操作一直无法完成假设原有的消费者迟迟没有重新发送“加入组请求”,协调者就无法确定何时才可以返
回“加入组响应”给已经发送了“加入组请求”的消费者 。 从创建延迟操作, 经过了“再平衡操作超时时间”之后,延迟操作会被强制完成。 在完成延迟操作时,
协调者会找出那些没有在规定时间内重新发送“加入组请求”的消费者,将它们从消费组中移除摊。 

  在发送“加入组响应”时,消费组中的所有消费者定都在“再平衡操作超时时间” 及时发送了“加入组请求” 。 协调者返回“加入组响应”给每个消费者后,
都会完成本次“延迟的心跳”,并调度下一次“延迟的心跳” “延迟的心跳”和“延迟的加入组”概念上相同 ,前者因为是消费者级别,超时时间
是消费者自己的会话超时时间 后者因为是消费组级别 ,超时时间是所有消费者的最大会话超时时间

  在消费组的次再平衡操作过程中,服务端的协调者只有个延迟的加入对象( DelayedJoin ),并且它会为每个消费者保存个延迟的心跳对
( DelayedHeartbeat 来监控消费者是否及时发送心跳,具体步骤如下 
(1)消费者发送“加入组请求”时会指定会话的超时时间(简称“会话时间”)
(2)协调者不能立即返回“加入组响应”给消费者,创建个消费组级别的“延迟加入”
(3)“延迟加入”可以完成,协调者返回“加入组响应”给消费组中的每个消费者
(4)协调者为每个消费者都创建个“延迟心跳”,并监控每个消费者是否存活

  协调者在处理完消费者入组请求”后,会返回“加入组响应”给消费者 消费者收到“加入组响应”后 , 就应该在会话时间内及时发送“同步组请求”给协调者 
,协调者就会认为消费者现了故障。 调者在处理“同步组请求”时,有多个方调用了“完成若调度下次心跳”方法 

(1)状态为“等待同步”,在设置成员元数据的回调方法后调用
(2)状态为“稳定 ,在发送“ 步组应”给消费者后调用
(3)状态为“等待同步”,收到主消费者的“同步组请求”,给每个消费者发送 同步组响应”后调用 
处的用法和协调者处理“入组请求”时 给每个消费者发送“入组H向应”后调用“完成并调度下一次心跳”方法类。 都针对所有消费者,而不是单个消费者 
但前面两个用法,只针对个消费者 

  协调者创建完“延迟操作”对象后,一个很重要的步骤是:当“延迟操作”相关的外部事件发生时 , 就需要通过延迟缓存尝试完成延迟的操作 
 对于“延迟的加入组”,外部事件是消费者发送了“加入组请求” ;对于“延迟的心跳” ,外部事件是协调者和消费者之间有网络通信 。 
不管是协调者处理消费者发送的请求,还是协调者发送应给消费者 ,协调者都会完成本次延迟的心跳, 并开始调度下一次延迟的心跳
协调者完成“延迟加入”操作的时间是 10:00:00 ,它为每个消费者创建的“延迟心跳”,会根据当前时间和消费者的会话超时时间,
设置下次“延迟心跳”的截止时间( deadline 。 

  消费者在收到“加入组响应”后,会发送“同步组请求”给协调者 协调者依次处理每个消费者的“同步组请求”,会先完成延迟缓存中已有的“延迟心跳”,然后创建新的“延迟心
跳” 这里假设协调者最后才处理主消费者的“ 同步组请求” 如果普通消费者发送“同步组请求”比主消费者还要晚,它会先完成已有 的“延迟心跳”,并创建新的“延迟心跳” 
但协调者在调用PropagateAssignment () 方法时,就不会为这样的普通消费者再次调用 “完成并调度一次心跳” 。 

延迟的心跳

  延迟操作有3个主要的方法:尝试完成方法(返回布尔值,表示是有可以完成)、超时的回调方法 、完成的回调方法对于“延迟加入”,

尝试完成是判断消费组成员中是否还有消费者没有重新发送“加入组请求”,如果全部都发送了“加入组请求”,就认为“延迟加入”可以完成。 “延迟加入”完成时
的回调方法会发送“加入组响应” 
“延迟心跳”的尝试完成方法( tryCompleteHeartbeat() )判断条件是:消费者成员是否存活 如果消费者存活,
则可以调用完成时的回调方法( tryCompleteHeartbeat()

判断消费者成员是否存活有下面的3种条件,只要任何一个条件满足,都认为消费者是存活的
- 消费者成员的 awaitingJoinCallback 回调方法不为
- 消费者成员的 awaitingSyncCallback回调方法不为
- 消费者成员最近的心跳时间加上会话超时时间大于下一次心跳的截止时间

1 . 完成并调度下一次心跳
  协调者通过checkAndCoMplete ()方法尝试完成已有的“延迟心跳” 。这一步一定能够完成“延迟的心跳”,否则就没有必要再创建新的“延迟的心跳”了 

  在完成了上次的“延迟心跳”后,协调者会计算出下一次的心跳截止时间,并创建新的“延迟心跳” 。 这一次通过tryCompleteElseWatch ()方法尝试完成刚刚
创建的“延迟心跳”,则定不能完成 。因为判断能够完成的条件是 : 最新的心跳时间加上会话超时时间必须大于下一次心跳的截止时间 。 
刚刚创建的“延迟心跳”对象,在计算这个条件时,“最新条件时间加上会话超时时间等于下次心跳的截止时间”,因此不满足完成的条件

  协调者处理“加入组请求”和 “同步组请求”时,调用“完成和调度下一次心跳”方法(下文简称“调度方法”)为例 。 有3个地方会调用该方法 
协调者返回“加入组响应”给每个消费者之后、协调者处理消费者的“同步组请求”时 协调者返回“同步组响应”给每个消费者之后,具体步骤如下 

(1)协调者发送“加入组响应”给某个消费者后 ,当前时间为 0秒 次调用调度方法,延迟缓存中没有“延迟心跳”,先创建“延迟的心跳”,
而且它的截止时间为5秒,不满足完成的条件,加入延迟缓存

(2)消费者在 1秒时发送了“同步组请求”,当前时间为 1秒 协调者处理消费者的“同步组请求”,第二次调用调度方法,延迟缓存中有“延迟的心跳”,尝试完成它,
 可以完成然后创建新的“延迟心跳”,截止时间为6秒。

(3)协调者发送“同步组响应”给某个消费者后 ,当前时间为 3秒 次调用调度方法,延迟缓存中有“延迟的心跳”,尝试完成它,可以完成。 
然后创建新的“延迟心跳”,截止时间为8秒我们从延迟缓存的角度看调度方法。 调度方法分3步 : 检查并完成延迟心跳、创建
新的延迟心跳、尝试完成并监视延迟的心跳 如果缓存中已经存在延迟操作,第一步一定会完成延迟的心跳,并将延迟心跳从缓存中删除 。 
步一定不会完成新创建的延迟心跳,并将刚创建的延迟心跳加入缓存。 

调度方法每次创建新的延迟心跳,都会更新截止时间 只要在下次心跳截止时之前执行调度方法,都会完成延迟的心跳。但如果没有在截止时 
间内再次执行调度方法,延迟缓存中的延迟心跳就会超时,对应的消费者就有可能被协调者从消费组中移除(还有下面分析的其他条件限制)

2. 判断消费者成员是否存活
  消费者成员元数据的回调方法有两个,先来看awaitingSyncCallback和延迟心跳示例 假设3个消费者设置的会话超时时间分别: 
C1=10秒, C2=20秒, C3=40秒。 协调者完成“延迟加入”,发送“加入组响应”的时间为 10:00:00对应每个消费者的下次心跳截止时间分别是: C 1=
10 : 00: 10 , C2 = 10: 00: 20, C3 = 10 : 00 : 40 C1在 10:00:03发送了“同步组请求”,协调者更新C1的心跳截止时间为 10 00 :13 
那么照理说, C1在 10:00 : 13后因为直都没有机会再调用调度方法,所以“延迟的心跳”就超时, C1就会被协调者从消费组中移除 
但实际上,协调者处理C1 的“同步组请求”时,设置了 awaitingSyncCallback回调方法 。 即使“延迟的心跳”超时了,但“回调方法不为”,消
费者成员仍然被认为是存活的, C1就不会从消费组中移除

  协调者调用“完成并调度下次心跳”的调度方法时,不管外部事件是哪种(返回加入组响应、处理同步组请求、返回同步组响应),
都会更新同一个延迟的心跳对象
  在任何时刻,消费者在延迟缓存中的延迟心跳只有一个 。 也就是说,协调者在返回“加入组响应”时,也可能会更新返回“同步 
                                                                                     

组响应”创建的延迟

再来看“判断消费者成员存活”的另一个条件: awaitingJoinCallback回调方法不为空。

  协调者在处理普通消费者的“同步组请求”时,除了设置awaitingJoinCallback回调方法,也会调用调度方法更新延迟的心跳
如果延迟心跳超时,但主消费者还没有发送“同步组请求”, 普通消费者仍然被认为是存活的 。协调者在处理消费者的“加入组请求”时,
会设置awaitingJoinCallback回调方法,但不会调用调度方法。 如果对应的延迟心跳超时,但延迟的加入操作还不能完成 ,消费者
也被认为是存活的

  假设有两个消费者,协调者在10:00 00返回同步组响应给 C1和 C2 ,它们的下次心跳截止时间分别是C110:00:10和C2= 10:00:2
 下面几个步骤是协调者处理“加入组请求”、延迟加入、延迟心跳相关的事件顺序 
(1新的消费者C3 在 10:00:02加入组, C1和 C2必须在心跳截止时间内重新发送“加入组请求”
(2 ) C110:00:03重新发送“入组请求”,延迟加入不能完成,因为C2还没有发送“加入组请求”
(3)当间到 10:00:10时(并不是 10:00:13 ), C1的延迟心跳超时了 但因为协调者处理“加入组
  请求”时,设置了 awaitingJoinCallback回调方法,所以C1还是存活的
()  C2在10:00:15重新发送了“加入组请求”,延迟加入可以完成协调者返回响应给3个消费者,
并且更新它们的下次心跳截止时间,分别是 : C1=10: 00: 25 、C2= 10:00:35 、C3= 10:00:55 。

假设消费者2并没有在下次心跳截止时间( 10:00:20 )之前重新发送“加入组请求”,对应的延迟心跳会判断到消费者2失败,从而将其从消费组中移除 
 另外,延迟的加入在完成时,协调者也不 会返回“加入组响应”给消费者2,因为它已经不在消费组中了

3. 协调者处理,心跳
  消费者成功加入组后,会在调用onJoinComplete ()回调方法后重置心跳任务,重新开始调度发送 心跳的定时任务 。 这里用“重置”是因为
消费组会经常发生再平衡,每次再平衡过后,消费组状态变为“稳定”,每个消费者都需要重新发送心跳请求给协调者 
协调者处理消费者发送的心跳请求,没有其他的依赖限制条件
协调者处理心跳请求和加入组过程中调用调度方法样,也会立即完成延迟缓存中已有的延迟心跳,并创建个新的延迟心跳并重新放入延迟缓存。 
最后,心跳的处理没有产生结果数据,协调者直接返回没有错误码的“心跳响应”给消费者 。 

posted @ 2018-10-29 14:49  青青子衿J  阅读(684)  评论(0编辑  收藏  举报