kafka之协调者

一、消费者加入消费组

1、加入组请求的业务逻辑主要步骤如下:

   (1)、消费者加入消费组之前,需要做一些准备工作,比如同步提交一次偏移量,执行监听器的回调。

   (2)、消费者创建“加入组请求”,包括消费者的元数据作为请求的数据内容。

   (3)、消费者发送“加入组请求”,采用组合模式返回一个新的异步请求对象,并定义回调处理器。

   (4)、客户端通过轮询,确保组合模式返回的异步请求必须完成,这是一个阻塞的方法。

     (5)、异步请求完成后,执行回调方法,将分区设置到消费者的订阅状态,并重置心跳定时任务。

涉及的方法ensureActiveGroup()包含有3个子流程:消费者准备加入消费组,发送"加入组请求"并定义响应处理器,消费者完成加入消费组。

2、“加入组请求”对象的变量有: 消费组名称(groupId) 、消费者成员编号(memberId)、协议类型(protocolType)、元数据(metadata)。

3、分区方式:范围(协议名称是range)、循环(协议名称是roundrobin)。

4、subscriptions 表示每个消费者的订阅信息,通过让每个消费者都发送向己的订阅信息给协调者,协调者就可以收集到所有消费者订阅的主题。metadata 是集群的元数据,它记录了每个主题的相关信息,包括主题的分区数。

注意: subscriptions 是订阅信息,消费者订阅了哪些主题。Assignor是分区分配器,它会执行分区分配的算法, Assignnment 是分配结果,是执行分配分区后的结果。

5、消费者分区分配的过程,具体步骤如下:

    (1)消费者发送订阅信息给协调者。

    (2)协调者收集所有的消费者,以及它们对应的订阅信息。

    (3)协调者执行任务分配算法,即具体如何将不同的分区分配给不同的消费者。

    (4)分配结果确定后,协调者将分区返回给消费者,消费者分配到分区开始t作。

改进后的具体步骤如下:

    (1)消费者发送订阅信息给协调者。

    (2)协调者收集所有的消费者,以及它们对应的订阅信息。

    (3)协调者将所有的消费者成员列表及其订阅信息发送给主消费者。

    (4)主消费者执行具体的分区分配算法。

    (5)主消费者将分配结果同步回协调者。

    (6)协调者收到主消费者的分配结果,将分区返回给每个消费者。

6、消费者获取协调者的分配结果的具体步骤如下:

     (1) 每个消费者都发送“加入组请求”给协调者节点。

     (2)协调者收到所有消费者发送的“加入组请求”,返回“ 加入组响应”给每个消费者,还会将执行分区分配算法需要的数据(比如消费者成员列表)传给主消费者。

    (3)主消费者执行完分区分配算法后,将“分配结果”通过“同步组请求” 的方式发送给协调者节点。其他消费者也会发送“ 同步组请求”给协调者,但是它们的请求中并没有“分配结果”数据。

    (4) 每个消费者从“ 同步组响应”的结果数据中获取到分配给它们的分区。

7、关于加入组、同步组、加入组响应处理器、同步组响应处理器的一些步骤如下:

    ( I )每个消费者都向协调者发送“加入组请求”,申请加入消费组。

    (2)协调者接收每个消费者的“加入组请求”,收集消费组的消费者成员列表。

    (3)协调者选举一个消费者客户端作为主消费者。

    (4 )协调者向发送“加入组请求”的每个消费者返回响应结果,其中包含所有消费者成员列表。

    (5 )步骤(3 )的主消费者会做额外的分区分配算法,并在计算完成后发送同步组请求给协调者。

    (6 )普通消费者收到包含成员列表的“加入组响应”结果不做计算,立即发送同步组请求给协调者。

    (7)不管是主消费者还是普通消费者,发送同步组请求的目的都是向协调者申请分区。

    (8)协调者收到主消费者在步骤( 5 )的分配结果,向每个发送同步组请求的消费者返回分区。

8、“加入组请求”和“同步组请求"的相关特点

     (1)、客户端收到服务端返回的晌应结果后,才会调用回调处理器的回调方法。

   (2)、使用组合模式返回的异步请求对象,只有调用其完成方法才表示异步请求已经完成。

      (3)、“加入组请求”先于“同步组请求”发送给协调者。

9、消费者发送“加入组请求”给协调者,到获取到分区列表的过程:

     (1 )、消费者发送“加入组请求”,得到一个“加入组”的异步请求。

     (2 )、消费者获得“加入组响应”结果,表示协调者已经收集到所有发送了“加入组请求”的消费者。

     (3 )、主消费者会执行分区分配任务,返回结果是消费组中所有消费者及其对应的分区列表。

     (4)、每个消费者都会发送“同步组请求”,得到一个“同步组”的异步请求。

    (5)、每个消费者获得“同步组响应”结果,表示分配给当前消费者的分区列表。

    (6 )、完成“同步组”的异步请求,并通过模式完成“加入组”的异步请求。

    (7 )、消费者获取“加入组”异步请求的结果,这个数据表示的就是分配给消费者的分区。

10、消费者加入组之前需要执行两个操作:

      (1 )禁用向动提交任务,因为在加入组过程中不会拉取和消费新消息,所以没必要提交偏移量。

      (2)执行一次同步提交偏移盘,这个操作是阻塞的,确保提交偏移量能够成功完成。

在加入组之后,也要执行下面几个操作。

     (1 )更新订阅状态的needsFetchCommittedOffsets变量,表示需要刷新分区的提交偏移量。

     (2)更新订阅状态的分配结果,为每个分区新创建分区状态,这个对象用来记录分区的最新状态。

    (3 )启动消费者的向动提交任务。

11、触发再平衡操作事件:

     (1)、消费者订阅的主题集合中任意一个主题的分区数量发生变化。

     (2)、创建或删除一个主题。

     (3)、消费组中已经存在的一个消费者成员挂掉了。

     (4)、一个新的消费者成员加入已经存在的消费组中。

12、KafkaConsumer具有提供轮询方法和偏移量的方法,与偏移量相关的方法如下:

     (1)、同步和异步提交偏移量: commitsync(offsets) 、commitAsync(offtsets )

     (2)、获取分区的拉取和提交偏移量: position(partition)、committed(partitiion) 。

     (3)、定位到指定的位置,更新拉取偏移量: seek(partition,position)。

“加入组和同步组”的请求和晌应对象:

二、协调者处理请求

1、KaafkaApis 是处理客户端各种请求的人口,但具体的请求处理则交给不同的服务类去实现。比如:

    (1)、服务端处理协调者相关的请求,交给消费组的协调者(GroupCoordinator);

    (2)、处理日志存储相关的请求,交给副本管理器(ReplicaManager);

       (3)、处理控制器相关的请求,交给控制器(KafkaController)

2、协调者是同一个消费组下所有消费者的协调节点。一个消费组有多个消费者,而消费组只是一个逻辑概念。具体涉及消费组相关的业务逻辑操作时必须有具体的实现类才能完成, 协调者就充当了这样的管理员角色。协调者会通过元数据的方式管理消费’组下的所有消费者。由于没有限制协调者只能作为一个消费组的协调节点,协调者可以同时管理多个消费组,所以元数据有两种:消费组的元数据、消费者的元数据。其中,消费组的元数据包括了所有消费者的元数。

注意: 协调者处理“加入组请求”和“同步组请求”,不需妥为每种请求都定义一个“成员元数据” 。协调者只用了一个统一的“成员元数据”表示“这个消费者在加入组过程中, 在服务端保存的相关状态数据” 。

3、协调者在处理“加入组请求”和“同步组请求”之前都需要优先做下吨的一些条件检查:

     (1)、协调者不可用,通常是协调者被关闭了。

     (2)、消费者客户端传递的消费组编号无效,比如没有设置消费组编号。

     (3)、消费者连接错了协调者,这个协调者不是消费组的协调者。

     (4)、协调者正在加载,通常是协调者自身在进行迁移。

     (5)、消费者客户端设置的会话超时时间无效。

     (6)、协调者还没有消费组,但消费者的成员编号却不是“未知编号” 。

     (7)、协调者有消费组,消费者的成员编号不是“未知编号”,但是不在消费组中。

条件检查过后, handleJoinGrouup()方法最终会调用doJoinGroup() 方法来允许消费者加入消费组,有下面两种情况:

       (1)、消费组为空井且成员编号为“未知编号”,允许加入。第一个消费者第一次加入组会执行一次。

     (2)、消费组不为空,如果成员编号是“未知编号”,允许加入;如果成员变量不是“未知编号”,必须保证已经在消费组中才允许加入。

注意:协调者在发送“加入组响应”之前就更新消费组状态为“等待同步”。另外,协调者往每个消费者发送响应结果之后,就立即针对该消费者启动服务端的心跳监控。

4、协调者处理“同步组请求”时, 将发送“同步组响应结果”给消费者的逻辑,作为一个回调方法传给了“延迟存储”对象(

DelayedStore) 。“延迟存储”由消息集和回调方法组成,协调者会先存储完“消费组分配结果” 代表的消息、集,然后才调用回调方法。即,协调者将“消费组分配结果”保存到内部主题之后, 才会发送“同步组响应”给每个消费者。协调者保存“消费组分配结果”和保存生产者发送的消息一样,都落到了副本管理器的追加消息流程上。服务端处理生产者消息的回调方法是发送响应给生产者,协调者保存完“消费组分配结果”后的回调方法是将“同步组响应”返回给消费者。

5、消费组的状态机转换。有4种状态: “稳定”“准备再平衡”“等待同步”“离开” 。在创建延迟操作对象之前,会更新消费组状态为“准备再平衡”;当延迟操作对象完成后,会更新消费组状态为“稳定”。

三、延迟的加入组操作

1、“延迟操作”,表示协调者会延迟发送“加入组响应”给消费者。但协调者不会为每个消费者的“ 加入组请求”都创建一个“ 延迟操作”,而是仅当消费组状态从“稳定”转变为“准备再平衡”,才创建一个“延迟操作”对象。

2、消费组元数据管理“消费者成员元数据” 的列表,为了保证列表操作(增加、删除、修改)的线程安全,需妥在“消费组元数据”对象上进行代码块的同步。另外,doJoinGroup()方法依次调用了addMemberAAndRebalance() 、maybePrepareRebalannce() 方法。maybePrepareRebalannce()方法也加了代码块同步,这里的加锁同步因为是在同一个线程内,所以是一个可重入锁。

3、延迟操作相关的结论

      (1)、延迟操作需要指定一个超时时间,表示在指定时间内没有完成时会被强制完成。

      (2)、延迟操作加入到延迟缓存中,会指定一个键。比如,和消费组相关的延迟加入,键是消费组编号。

      (3)、服务端创建延迟操作后,通常会有“尝试完成延迟操作”的动作(延迟操作如果能够尽早完成是最好的) 。尝试完成延迟操作的外部事件会有多种情况,而且因为延迟操作有依赖条件,所以任何可能改变依赖条件的事件,都应该执行“尝试完成延迟操作” 。比如,协调者因为依赖了“等待消费者发送加入组请求”这个条件才会创建“延迟的加入组”对象。如果有消费者发送了加入组请求,就应该尝试完成“延迟的加入组”对象。

      (4)、当外部事件尝试完成延迟操作时,怎么判断延迟操作能不能完成?不同的延迟操作类型因为依赖条件不同,应该自定义可以完成延迟操作的条件判断。

4、延迟操作对象相关的方法:

   (1)、tryComplete()尝试完成,如果不能完成,返回false ,表示延迟操作还不能完成。

     (2)、onnComplete()延迟操作完成时的回调方法,完成有两种:正常主动完成和超时被动完成。

     (3)、onExpiration()延迟操作超时的回调方法,如果之前一直调用尝试完成都不能完成,在指定的坦时时间过去后就会强制完成。调用用这个回调方法, 一定会再调用onnComplete()方法。

5、延迟缓存的相关方法:

(1) 、tryCompleteElseWatch(operatoin,key)尝试完成延迟的操作,如果不能完成就以指定的键监控这个延迟操作。创建完延迟操作对象后,可以立即尝试完成,不一定只能由其他事件尝试完成。

(2)、checkAndComplete(key ) 。检查并尝试完成指定键的延迟操作,在上一个方法中,如果延迟操作没有完成,会被加入到延迟缓存中。

6、延迟操作需要根据状态数据判断是否可以完成:

   (1)、“延迟心跳操作”的状态数据有: 消费组元数据、消费者元数据。消费者元数据会被协调者用来判断这个消费者是否及时发送了心跳。消费组元数据会用在:当消费者没有及时发送心跳,需要将对应的消费者元数据从消费组元数据中移除时。另外,消费组元数据对象还会用在加锁同步代码块上。

(2)、“延迟加入操作”的状态数据有:消费组元数据。因为协调者判断延迟加入操作是否能够完成的依据是: 消费’组中的所有消费者成员是否都发送或重新发送了“加入组请求” 。

7、第一个消费者发送“加入组请求”给协调者,具体的处理步骤:

    (1)协调者处理第一个消费者的“加入组请求”,会创建消费者成员元数据,并加入消费组元数据。

    (2)消费组初始为“稳定”状态,开始再平衡操作,将状态改为“准备再平衡” 。

    (3)创建一个“延迟的加入组”对象,并立即通过“延迟缓存”尝试完成刚创建的延迟操作。

    (4)由于消费组中所有消费者成员(目前只有第一个消费者) 的值对象不为空, notYetRejoinedMembers()方法没有收集到任何元素,返回值为空,满足“完成延迟操作”的条件。

    (5)因为可以完成延迟的操作,所以强制完成方法会调用延迟操作对象的onCompleteJoiin()方法。

    (6)延迟操作对象在完成时的回调方法,会首先将消费组状态更新为“等待同步” 。

    (7)返回“加入组响应”给所有的消费者(这里还是只有第一个消费者) 。

 四、消费组状态机

1、协调者保存的消费组元数据中记录了消费组的状态机, 消费组状态机的转换主要发生在“加入组请求”和“同步组请求”的处理过程中。除此之外,协调者处理“离开消费组请求”“迁移消费组请求”“心跳请求” “提交偏移量请求”也会更新消费组的状态、机,或者依赖消费组的状态进行不同的处理。

消费者要加入消费组, 需要依次发送“加入组请求”和“同步组请求”给协调者。消费者加入组的过程叫作再平衡,因为协调者处理这两种请求会更新消费组状态,所以再平衡操作跟消费组状态也息息相关。

2、监昕器回调方法和再平衡操作的执行顺序如下:

   (1)消费者准备加入组,调用“消费者再平衡监昕器”的onPartitionsRevoked()方法。

   (2)消费者发送“加入组请求” , 协调者开始处理消费者的“加入组请求”,执行再平衡操作。

   (3) 消费者完成加入组,调用“消费者再平衡监昕器”的onPartitionsAssiignned()方法。

3、一个消费者加入消费组的步骤:

    (1)消费者发送“加入组请求”,消费组的状态从初始的“稳定”更改为“准备再平衡”,这个过程会创建一个延迟的操作,并检查能否完成延迟操作。

    (2)因为只有一个消费者,所以延迟操作可以完成,消费组状态从“准备再平衡”改为“等待同步” 。

    (3)当前的状态是“等待同步”,表示协调者等待消费者发送“同步组请求” 。当协调者收到主消费者发送的“同步组请求”后,会返回“同步组响应”给消费者,消费组状态从“ 等待同步”改为“稳定” 。

4、协调者处理这种消费者重新发送的“加入组请求”时,因为消费组状态已经是“稳定”,所以它会立即返回“加入组响应”给消费者。具体步骤如下:

     (1)3个消费者发送“加入组请求”给协调者,消费组状态从“准备再平衡”到“等待同步” 。

     (2)协调者返回“加入组响应”给3个消费者,前两个消费者收到“加入组响应”,第三个没收到响应。

     (3)第二个消费者是普通消费者,它发送“同步组请求”,协调者处理时只是设置对应的回调方法。

    (4)第一个消费者是主消费者,它完成分区分配,发送“同步组请求”给协调者,消费组状态改为“稳定” 。

    (5)协调者返回“同步组响应”给两个消费者,因为只有前两个消费者的回调方法不为空。

    (6)第三个消费者没收到“加入组响应",它重新发送“加入组请求”

    (7)协调者处理第三个消费者的“加入组请求”,消费组状态是“稳定”,立即返回“加入组响应” 。

    (8)第三个消费者因为不是主消费者,它收到“加入组响应”,会立即发送“同步组请求” 。

    (9)协调者处理第三个消费者的“同步组请求”,消费组状态是“稳定”,立即返回“同步组响应“

5、主消费者没有收到“加入组响应”的过程:

    (!) 3 个消费者发送“加入组请求”给协调者,消费组状态从“准备再平衡”到“等待同步” 。

    (2)协调者返回“加入组响应”给3个消费者,前两个消费者收到“加入组响应",第三个没收到。

    (3)前两个消费者都是普通消费者,它发送“同步组请求”,协调者处理时只是设置对应的回调方法。

    (4) 主消费者没有收到“加入组响应”,重新发送“加入组请求” 。

    (5)协调者处理主消费者的“加入组请求”,消费组状态是“等待同步”, 立即返回“加入组响应” 。

    (6)主消费者收到“加入组响应”,执行分区分配丁. 作,并发送“同步组请求” 。

    (7)协调者处理主消费者的“同步组请求”,返回响应给所有消费者,更新消费组状态为“稳定” 。

6、看消费组状态为“准备再平衡”的处理步骤:

      (1 )第一个消费者加入组,消费组状态为“等待同步”,第一个消费者同时作为主消费者还在执行分区分配工作。

     (2)第二个消费者作为新的消费者加入组,将消费组状态改为“准备再平衡”

     (3)第一个消费者执行完分区分配工作,会发送“同步组请求”给协调者。

     (4)协调者处理第一个消费者的“同步组请求”,由于消费组状态是“准备再平衡”,它会返回“正在再平衡”的错误码给第一个消费者。

    (5)第一个消费者收到错误的“同步组响应”,会重新发送“加入组请求:

7、消费者客户端的工作如下:

    (1)、取消定时心跳任务,因为离开组意昧着不被协调者官:理,就不需要向协调者发送心跳了。

      (2 )通过消费者的协调者对象(ConsumerCoordinnator)发送“离开组请求”给协调者。

      (3 )重置相关的信息,比如设置成员编号为“未知编号”、重置rejoinNeeded变量为false。

8、协调者等待“延迟操作”完成有一个时间限制,它会选择消费组巾所有消费者会话超时时间的最大值,作为“再平衡操作的超时时间”,也叫作“延迟操作的超时时间”。

“延迟的心跳”是消费者级别,超时时间是消费者自己的会话超时时间;

“延迟的加入组”是消费组级别,超时时间是所有消费者的最大会话超时时间。

9、消费组再平衡操作过程:

      (1 )消费者发送“加入组请求”时会指定会话的超时时间(简称“会话时间”)。

      (2 )协调者不能立即返回“加入组响应”给消费者,创建一个消费组级别的“延迟加入” 。

      (3 )“延迟加入”可以完成,协调者返回“加入组响应”给消费组中的每个消费者。

      (4)协调者为每个消费者都创建一个“延迟心跳”,并监控每个消费者是否存活。

10、延迟操作有3个主要的方法:

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

判断消费者成员是否存活有下面的3 种条件,只要任何一个条件满足,都认为消费者是存活的。

       (1)、消费者成员的awai.ti.ngJoi.nCallback 回调方法不为空。

       (2)、消费者成员的awai.ti.ngSyncCallback 回调方法不为空。

      (3)、消费者成员最近的心跳时间加上会话超时时间大于下一次心跳的截止时间。

11、概念:

    (1)、加入组请求:协调者收集消费组的所有消费者,并选举一个主消费者执行分区分配工作。

    (2)、同步组请求:主消费者完成分区分配,由协调者将分区的分配结果传播给每个消费者。

    (3)、“ 准备再平衡” :新消费者加入组或者旧消费者离开组消费组都需要执行一次再平衡操作。

    (4)、“ 等待同步”:所有消费者都加入组,协调者返回“加入组响应”给每个消费者前,更改状态为“等待同步飞它表示协调者等待接收主消费者发送的包含消费组分配结果的“同步组请求” 。

    (5)、“ 稳定”:协调者返回带有分区分配结果的“同步组响应”给每个消费者。

12、协调者除了管理消费者的负载均衡,并最终分配分区给每个消费者,还会接收每个消费者的心跳请求。协调者通过心跳监控消费者成员是否存活:如果消费者没有在指定的截止时间内发送心跳,协调者认为消费者失败,将其从消费组中移除,这样消费组就需要执行再平衡操作。另外,协调者在处理“加入组请求”和“同步组请求”过程中,为了保证参与加入组的消费者及时响应,也会用心跳来监控消费者成员是否还存活。

--------------------  学习笔记  --------------------------

猜你喜欢

转载自blog.csdn.net/baidu_28068985/article/details/106983567