Kafka技术内幕 读书笔记之(五) 协调者——协调者处理请求

  消费者客户端使用“消费者的协调者对象”( ConsumerCoordinator )来代表所有和服务端协调者节点有关的请求处理,比如心跳请求、
获取和提交分区的偏移量(自动提交任务)、发送“加入组请求”和“同步组请求”从协调者获取到分区 。 服务端处理客户端请求的人口都是KafkaApis类,
它会针对不同的请求类型分发给不同的方法处理 。 

服务端定义发送响应结果的回调方法
  不同消费者在不同时刻发送请求给服务端,服务端并不会立即发送响应结果给消费者为了保证服务端的高性能,虽然服务端不能立
回响应结果给消费者但并不意味着服务端对每个请求的处理都是阻塞的 那么既要做到不能阻塞请求的处理,又要做到必须返回响应结果给消费
者,服务端的做法是:在处理每个请求时,首先定义一个“发送应结果的回调方法”( sendResponseCallback() ),回调方法会传给负责消费组
相关业务逻辑的消费组协调者( GroupCoordinator 。 协调者认为请求完成时,会调用回调方法发送响应结果给消费者 

  么要在处理请求开始就定义回调方法,而不是在请求真正完成后直接发送响应结果,这样就不需要回调方法了 。 这是因为务端发送
应结果给客户端,会个和请求互相关联的应结果对象,确保“客户端发送给务端的请求” 和 “服务端返回给客户端的响应结果 ” 是在同一个
网络通道中完成应结果需要持有请求的引 用” 如果没有在处理请求的地方定义回调方法 ,而是在请求完成直接应结果并
发送给客户端 就需要请求对象路传到请求完成的方才可以 。 

  服务端处理“加入组请求”中回调方法的参数是“加入组的结果”,然后封装成“加入组的响应”返回给客户端。处理“同步组请求”中回调方法的参数是
“成员状态”, 即分配的分区,然后封装成“同步组的响应”返回给客户端 。客户端从服务端接收的响应结果和服务端返回给客户端的响应结果必须一样  

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

消费者和消费组元数据
  消费者加入组过程发送的“加入组请求”和“同步组请求”,都会指定消费组编号( groupid )和消费者成员编号(memberId ),同一个消费组编号只
对应一个“消费组元数据”( GroupMetadata ,下文简称“组元数据”)。 服务端使用“消费者成员元数据”( MemberMetadat,下文简称“成员元数据”)
表示每个消费者发送的元数据信息,并添加到对应的“组元数据”中

1. 消费者成员元数据
  “成员元数据”类的构造函数参数,有成员编号 消费组编号、协议元数据集 会话超时时间是由客户端发送“加入组请求 ”时指的,latestHeartbeat变量
记录了该消费者最近一次发送心跳的时间 另外,“成员元数据”最重要的个信息是 : 当前这个消费者到底分配到了哪些分区( assignment变量)
因为消费者加入组的最终目的就是从协调者获取到分区 

  “成员元数据”还定义了两个值对象,它们分别对应服务端在处理请求定义的两个发送响应回调方法 : 
- awaitingJoinCallback:加入组回调方法

awai.ti.ngSyncCallback :同步组回调方法

2. 消费组元数据
  个“组元数据管理了所有消费者“成员元数据” 果添加“成员元数据” 时都还没有“组元数据” (更新一定存在“组元数据”),就会先建 组元数据” 
建“组元数据”是必须的,如果没有“组元数据”, 使有“成员元数据”,也是没有意义的

  “组元数据”在消费者需要加入或更新时,除了更新对应消费者的“成员元数据,还会记录些其数据。 比如,协调者会为消费组选择个主消费者 
 来代替它行分区分配工作 另外,每个消费者发送“加入组请求时,都会指定一个会话超时时间。协调者会从消费组的所有消费者中,选择
个最大的话超时时间,作为“再平衡操作的超时时间

  消费组元数据中还有一个很重要的数据 “消费组的当前状态” 因为每个消费者加入消费组都分成“加入组”和“同步组”两个步骤,所以协调者在处理不
同消费者的这两种请求时,都需要改变消费组的状态 。 消费组元数据的状态机有 4 种状态:“稳定状态”( Stable )、“准备再平衡状态”( preparingRebalance )、
“等待同步状态”( AwaitingSync )、“离开状态”( Dead 协调者新创建个消费组元数据,这个消费组元数据的初始状态为“稳定状态” 。 

协调者处理请求前的条件检查
  协调者在处理“加入组请求”和“同步组请求”之前都需要优先做下面的一些条件检查
- 协调者不可用,通常是协调者被关闭了
- 消费者客户端传递的消费组编号无效,比如没有设置消费组编号
- 消费者连接错了协调者,这个协调者不是消费组的协调者
- 协调者正在加载,通常是协调者自身在进行迁移
- 消费者客户端设置的会话超时时间无效
- 协调者还没有消费组,但消费者的成员编号却不是“未知编号”
- 协调者有消费组,消费者的成员编号不是“未知编号”,但是不在消费组中

  协调者针对上面几种异常情况都有特定的错误码,并且会即调用定义好的回调方法,把错误信息及时地返回给消费者客户端
消费者客户端在响应处理器的回调方法中,针对每种错误码都有不同的处理。 比如,如果是“未知编号”错误码,就会重置客户端的成员编号为“未知编号”,
然后重新发送“加入组请求”;如果是GROUP_COORDINATOR_NOT_AVAILABLE或NOT_COORDINATOR_FOR_GROUP , 消费者就会连接新的协调者节点,
并重新发送“加入组请求”
允许消费者加入消费组,有下面两种情况 
 
- 消费组为空井且成员编号为“未知编号”,允许加入 第一个消费者第一次加入组会执行一次
- 消费组不为,如果成员编号是“未知编号”,允许加入;如果成员变量不是“未知编号”,必须保证 已经在消费组中才允许加入。

  协调者处理消费者发送的“同步组请求”同样需要执行条件检查 客户端发送“加入组请求”后才会发送“同步组请求”,服务端处理“同步组请求”也一定在处理
“加入组请求”之后 协调者在正常处理“加入组请求”时一定会创建消费组,在处理“同步组”请求时必须保证消费组不为空。

  消费者发送“加入组请求”和“同步组请求”一定是发送到同一个协调者节点 协调者在判断有没有消费组之前,会首先判断它是不是消费组的协调者,
如果不是,直接返回 NOT_COORDINATOR_FOR_GROUP错误码 协调者处理两种情况的最终目的是 返回“加入组响应”和“同步组响应给消费者客户端
其中,“加入组响应”要能够返回所有的消费者成员信息,“同步组应”要能返回消费者的分配信息

协调者调用回调方法发送晌应给客户端

1 . 发送“加入组响应”给消费者
  协调者要返回“加入组应”给消费组下的所有消费者之前,会增加纪元编号 选择出个统一的消费组协议 、 将消费组状态更改为“等待同步” 
因为是要返回“加入组响应”,而消费者成员元数对象保存了 发送响应的回调方法”,所以只要用调用方法的方式调用值对象,就可以调用到“发送加入 
组响应的回调方法” 

  消费组管理了所有的消费者成员, 调者发送“加入组应”时,是次性起发送响应结果给每个消费者 。但是协调者处消费者发送的“
组请求”并不是同时进行的,这说明协调者在处理某些消费者的“入组请求”时,并不会立返回“入组响应” 。 实际上,这通过“延迟操作”
来实的,“延迟操作”类似于延迟的任务,它和消费组的状态机也有关系

2 发送“同步组响应”给消费者
  协调者没有同时处理每个消费者的“同步组请求”,但最后同时发送了“同步组响应” 。 说明协调者在处理某些消费者的“同步组请求”时,
并不会立即返回“同步组响应” 

  协调者收到主消费者的“同步组请求 ,它会立即返回“同步组响应”给所有的消费者(包括主消费者和普通消费者)
协调者发送“ 步组响应”给消费组每个消费者的方式和发送 加入组响应” 似。

3. 协调者保存消费组任务
  协调者在返回“同步组响应”给消费者之前,会先把“消费组分配结果”( groupAssignment )以普通消息的形式持久化到内部主题(_consumer_offsets )中
如果协调节点出现问题需要进行故障迁移,新的协调者可以从“内部主题” 中读取持久化的消息,重建“消费组分配结果” 。

  一个协调者可以充当多个消费组的协调节点,并使用“消费组缓存”保存它管理的所有“消费组元数据” 。迁移协调者时会读取内部主题的“消费组分配结果”,
 重新加载到“消费组缓存”中 。 协调者处理“入组请求”和“同步组请求”时,根据消费者客户端传递的消费组编号查询“消费组元数据” ,会先从
“消费组缓存”中查询,如果“消费组元数据”已经存在,直接使用现有的数据 。 

    协调者将“消费组分配结果”保存到部主题之后  才会发送“同步组应”给每个消费者

猜你喜欢

转载自www.cnblogs.com/jixp/p/9856869.html