消费者客户端使用“消费者的协调者对象”( ConsumerCoordinator )来代表所有和服务端协调者节点有关的请求处理,比如心跳请求、
获取和提交分区的偏移量(自动提交任务)、发送“加入组请求”和“同步组请求”从协调者获取到分区 。 服务端处理客户端请求的人口都是KafkaApis类,
它会针对不同的请求类型分发给不同的方法处理 。
服务端定义发送响应结果的回调方法
不同消费者在不同时刻发送请求给服务端,服务端并不会立即发送响应结果给消费者。 为了保证服务端的高性能,虽然服务端不能立即返
回响应结果给消费者但并不意味着服务端对每个请求的处理都是阻塞的 。 那么既要做到不能阻塞请求的处理,又要做到必须返回响应结果给消费
者,服务端的做法是:在处理每个请求时,首先定义一个“发送响应结果的回调方法”( sendResponseCallback() ),回调方法会传给负责消费组
相关业务逻辑的消费组协调者( GroupCoordinator )。 当协调者认为请求完成时,会调用回调方法发送响应结果给消费者 。
为什么要在处理请求刚开始就定义回调方法,而不是在请求真正完成后直接发送响应结果,这样就不需要回调方法了 。 这是因为服务端发送响
应结果给客户端,会创建一个和请求互相关联的响应结果对象,确保“客户端发送给服务端的请求” 和 “服务端返回给客户端的响应结果 ” 是在同一个
网络通道中完成的 。 “创建响应结果时需要持有请求的引 用” , 如果没有在处理请求的地方定义回调方法 ,而是在请求完成时直接创建响应结果并
发送给客户端 , 就需要把请求对象一路传到请求完成的地方才可以 。
服务端处理“加入组请求”中回调方法的参数是“加入组的结果”,然后封装成“加入组的响应”返回给客户端。处理“同步组请求”中回调方法的参数是
“成员状态”, 即分配的分区,然后封装成“同步组的响应”返回给客户端 。客户端从服务端接收的响应结果和服务端返回给客户端的响应结果必须一样 。
协调者是同一个消费组下所有消费者的协调节点 。 一个消费组有多个消费者 ,而消费组只是一个逻辑概念 。 具体涉及消费组相关的业务逻辑操作时
必须有具体的实现类才能完成 , 协调者就充当了这样的管理员角色。 协调者会通过元数据的方式管理消费’组下的所有消费者 。 协调者可以同时管理多个消费组,
所以元数据有两种:消费组的元数据 、消费者的元数据。 消费组的元数据包括了所有消费者的元数据。
消费者和消费组元数据
消费者加入组过程发送的“加入组请求”和“同步组请求”,都会指定消费组编号( groupid )和消费者成员编号(memberId ),同一个消费组编号只
对应一个“消费组元数据”( GroupMetadata ,下文简称“组元数据”)。 服务端使用“消费者成员元数据”( MemberMetadata ,下文简称“成员元数据”)
表示每个消费者发送的元数据信息,并添加到对应的“组元数据”中 。
1. 消费者成员元数据
“成员元数据”类的构造函数参数,有成员编号 、 消费组编号、协议元数据集 。 会话超时时间是由客户端发送“加入组请求 ”时指定的,latestHeartbeat变量
记录了该消费者最近一次发送心跳的时间 。 另外,“成员元数据”最重要的一个信息是 : 当前这个消费者到底分配到了哪些分区( assignment变量),
因为消费者加入组的最终目的就是从协调者获取到分区 。
“成员元数据”还定义了两个值对象,它们分别对应服务端在处理请求时定义的两个发送响应回调方法 :
- awaitingJoinCallback:加入组回调方法
- awai.ti.ngSyncCallback :同步组回调方法
2. 消费组元数据
一个“组元数据” 管理了所有消费者的 “成员元数据” 。 如果添加“成员元数据” 时都还没有“组元数据” (更新时一定存在“组元数据”),就会先创建 “组元数据” 。
创建“组元数据”是必须的,如果没有“组元数据”, 即使有“成员元数据”,也是没有意义的 。
“组元数据”在消费者需要加入或更新时,除了更新对应消费者的“成员元数据” ,还会记录一些其他数据。 比如,协调者会为消费组选择一个主消费者 ,
来代替它执行分区分配工作 。 另外,每个消费者发送“加入组请求时,都会指定一个会话超时时间。协调者会从消费组的所有消费者中,选择
一个最大的会话超时时间,作为“再平衡操作的超时时间 ” 。
消费组元数据中还有一个很重要的数据 : “消费组的当前状态” 。 因为每个消费者加入消费组都分成“加入组”和“同步组”两个步骤,所以协调者在处理不
同消费者的这两种请求时,都需要改变消费组的状态 。 消费组元数据的状态机有 4 种状态:“稳定状态”( Stable )、“准备再平衡状态”( preparingRebalance )、
“等待同步状态”( AwaitingSync )、“离开状态”( Dead )。 协调者新创建一个消费组元数据,这个消费组元数据的初始状态为“稳定状态” 。
协调者处理请求前的条件检查
协调者在处理“加入组请求”和“同步组请求”之前都需要优先做下面的一些条件检查 。
- 协调者不可用,通常是协调者被关闭了 。
- 消费者客户端传递的消费组编号无效,比如没有设置消费组编号 。
- 消费者连接错了协调者,这个协调者不是消费组的协调者。
- 协调者正在加载,通常是协调者自身在进行迁移 。
- 消费者客户端设置的会话超时时间无效。
- 协调者还没有消费组,但消费者的成员编号却不是“未知编号” 。
- 协调者有消费组,消费者的成员编号不是“未知编号”,但是不在消费组中