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

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

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

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

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

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

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 )中
如果协调节点出现问题需要进行故障迁移,新的协调者可以从“内部主题” 中读取持久化的消息,重建“消费组分配结果” 。

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

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

Kafka技术内幕 读书笔记之(五) 协调者——协调者处理请求的更多相关文章

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

    协调者保存的消费组元数据中记录了消费组的状态机 , 消费组状态机的转换主要发生在“加入组请求”和“同步组请求”的处理过程中 .协调者处理“离开消费组请求”“迁移消费组请求”“心跳请求” “提交偏移量请 ...

  2. Kafka技术内幕 读书笔记之(五) 协调者——延迟的加入组操作

      协调者处理不同消费者的“加入组请求”,由于不能立即返回“加入组响应”给每个消费者,它会创建一个“延迟操作”,表示协调者会延迟发送“加入组响应”给消费者 . 但协调者不会为每个消费者的 “加入组请求 ...

  3. Kafka技术内幕 读书笔记之(五) 协调者——消费者加入消费组

    消费者客户端轮询的3个步骤:发送拉取请求,客户端轮询,获取拉取结果 . 消费者在发送拉取请求之前,必须首先满足下面的两个条件.- 确保消费者已经连接协调者, 即找到服务端中管理这个消费者的协调者节点 ...

  4. Kafka技术内幕 读书笔记之(四) 新消费者——消费者提交偏移量

    消费组发生再平衡时分区会被分配给新的消费者,为了保证新消费者能够从分区的上一次消费位置继续拉取并处理消息,每个消费者需要将分区的消费进度,定时地同步给消费组对应的协调者节点 .新AP I为客户端提供了 ...

  5. Kafka技术内幕 读书笔记之(六) 存储层——服务端处理读写请求、分区与副本

    如下图中分区到 日 志的虚线表示 : 业务逻辑层的一个分区对应物理存储层的一个日志 . 消息集到数据文件的虚线表示 : 客户端发送的消息集最终会写入日志分段对应的数据文件,存储到Kafka的消息代理节 ...

  6. Kafka技术内幕 读书笔记之(四) 新消费者——新消费者客户端(二)

    消费者拉取消息 消费者创建拉取请求的准备工作,和生产者创建生产请求的准备工作类似,它们都必须和分区的主副本交互.一个生产者写入的分区和消费者分配的分区都可能有多个,同时多个分区的主副本有可能在同一个节 ...

  7. Kafka技术内幕 读书笔记之(三) 消费者:高级API和低级API——消费者消费消息和提交分区偏移量

    消费者拉取钱程拉取每个分区的数据,会将分区的消息集包装成一个数据块( FetchedDataChunk )放入分区信息的队列中 . 而每个队列都对应一个消息流( KafkaStream ),消费者客户 ...

  8. Kafka技术内幕 读书笔记之(一) Kafka入门

    在0.10版本之前, Kafka仅仅作为一个消息系统,主要用来解决应用解耦. 异步消息 . 流量削峰等问题. 在0.10版本之后, Kafka提供了连接器与流处理的能力,它也从分布式的消息系统逐渐成为 ...

  9. Kafka技术内幕 读书笔记之(六) 存储层——日志的读写

    -Kafka是一个分布式的( distributed ).分区的( partitioned ).复制的( replicated )提交日志( commitlog )服务 . “分布式”是所有分布式系统 ...

随机推荐

  1. 第四十天 并发编程之io模型

    一.今日内容 1.网络IO的两个阶段 waitdata copydata 2阻塞IO模型 之前写的都是阻塞 无论多线程 多进程 还是 进程池 线程池 3.非阻塞IO模型 在非阻塞IO中 需要不断循环询 ...

  2. Django RBAC用户权限设计方案

    RBAC基于用户权限系统设置方案 RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干 ...

  3. [USACO2008 Mar]土地购买

    传送门:>HERE< 题意:购买一组土地的费用是最长的长乘以最长的宽.现给出n块土地,求购买所有土地(可以将土地分为任意组,不需按顺序)的最小费用 解题思路 动态规划+斜率优化 斜率优化在 ...

  4. 网络流24(san)题题解汇总

    开坑(烂尾预定 1.餐巾计划问题 题解 2.最小路径覆盖问题 题解 3.试题库问题 题解 4.[CTSC1999]家园 题解 5.骑士共存问题 题解 6.最长不下降子序列问题 题解 7.深海机器人问题 ...

  5. 【CodeForces 730H】Delete Them

    BUPT 2017 summer training (for 16) #1E 题意 找到匹配要删除的文件名们但不匹配其它文件名们的表达式.其中?匹配所有字符,其它字符匹配本身. 题解 如果某个位置出现 ...

  6. [国家集训队]整数的lqp拆分

    我们的目标是求$\sum\prod_{i=1}^m F_{a_i}$ 设$f(i) = \sum\prod_{j=1}^i F_{a_j}$那么$f(i - 1) = \sum\prod_{j=1}^ ...

  7. 【Hihocoder1413】Rikka with String(后缀自动机)

    [Hihocoder1413]Rikka with String(后缀自动机) 题面 Hihocoder 给定一个小写字母串,回答分别把每个位置上的字符替换为'#'后的本质不同的子串数. 题解 首先横 ...

  8. 【转】设置 vim 显示行号永久有效

    在linux环境下,vim是常用的代码查看和编辑工具.在程序编译出错时,一般会提示出错的行号,但是用vim打开的代码确不显示行号,错误语句的定位非常不便.那么怎样才能让vim显示代码的行号呢? 1 临 ...

  9. [SCOI2014]方伯伯的OJ(线段树)

    方伯伯正在做他的Oj.现在他在处理Oj上的用户排名问题.Oj上注册了n个用户,编号为1-n“,一开始他们按照编号排名. 方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号: 1.操作格式为 ...

  10. LVS搭建负载均衡(二)DR模型

    应用场景:LVS配置负载均衡方式之一:dr 测试环境: 配置步骤: 1. 在主机lvs上安装ipvsadm ~]# yum install ipvsadm -y ~]# ipvsadm //启动:该命 ...