Kafka 2.3发布后官网的Consumer参数中增加了一个新的参数:group.instance.id。下面是这个参数的解释:

A unique identifier of the consumer instance provided by end user. Only non-empty strings are permitted. If set, the consumer is treated as a static member, which means that only one instance with this ID is allowed in the consumer group at any time. This can be used in combination with a larger session timeout to avoid group rebalances caused by transient unavailability (e.g. process restarts). If not set, the consumer will join the group as a dynamic member, which is the traditional behavior.

大致意思是:它是用户指定的一个consumer成员ID。每个消费者组下这些ID必须是唯一的。一旦设置了该ID,该消费者就会被视为是一个静态成员(Static Member)。静态成员配以较大的session超时设置能够避免因成员临时不可用(比如重启)而引发的Rebalance。由此可见,消费者组静态成员是2.3版本新引入的一个概念,主要是为了避免不必要的Rebalance。

 Rebalance Recap

之前我们在Kafka消费者组一文中讨论过Rebalance机制。它的主要作用是为消费者组下所有成员分配分区。Client端和Broker端需要同时参与到Rebalance过程。在Broker端,Coordinator组件负责处理成员管理,比如处理组成员发送的JoinGroup请求、SyncGroup请求、Heartbeat请求和LeaveGroup请求;在Client端,Leader Consumer成员接收Coordinator发送的成员订阅信息,然后根据一定的策略(Range/Round-Robin/Sticky/自定义)制定分配方案。

Rebalance发生的条件有三个:

  • 成员数量发生变化,即有新成员加入或现有成员离组(包括主动离组和崩溃被动离组)
  • 订阅主题数量发生变化
  • 订阅主题分区数量发生变化

其实,后两个条件可以合并成一个,即Rebalance触发条件只有两个:1. 成员数量发生变化;2. 订阅信息发生变化。

Rebalance的流程在那篇文章中也谈到了:首先,各个成员发送JoinGroup请求入组,Coordinator会等待一段时间等它们加入——这段时间由所有成员中max.poll.interval.ms的最大值来决定(在Kafka Connect中则是有专属的参数rebalance.timeout.ms来指定)。之后各成员发送SyncGroup请求等待Coordinator发送分配方案,然后开始正常消费。在消费的同时,各个consumer还会定期(heartbeat.interval.ms)上报心跳,告诉Coordinator组件它还活着。

 Issues for Rebalance

在实际场景中,因为成员离组而发生的Rebalance应该算是最多的,但有些场景下的Rebalance是非常不合理的。比如我们公司就有这样的痛点:Consumer的处理逻辑发生变更,必须要更新代码重新上线,此时就要引发Rebalance,但其实重启Consumer也许只需要几分钟而已,也就是说我的消费只要中断几分钟就可以了,Kafka完全没必要为这个就触发一轮Rebalance,更没有必要重新分配分区,维持之前的分配方案足矣。虽然社区提供的Sticky分配方案在一定程度上能够缓解此问题,但Rebalance的Stop The World(STW)的特性还是决定了生产环境中Rebalance越少越好。

Static Member

在目前的Rebalance设计中,消费者组下的每个实例都会被Coordinator分配一个成员ID,即member.id。很多Kafka用户都有过这样的疑问:我能手动设置这个member.id吗?很遗憾,这个memberID是Kafka自动生成的,在静态成员被引入前,规则是client.id-UUID,这里的client.id就是Consumer端参数client.id的值,而且这个ID会随着每轮Rebalance发生变化的。换句话说,Coordinator无法持久化地保存某个consumer实例的member.id。我想这可能是制约Rebalance时所有成员必须强制重新加入的部分原因,因为Coordinator无法记住每个成员都是谁。如果你看源代码,可以发现在每次Client重启回来发送JoinGroup时,它会封装一个UNKNOWN_MEMBER_ID的空串,没有任何有意义的信息给到Broker端。Coordinator接收到后只能把它当做是一个全新的成员。相反地,如果member.id能够被记住,那么Coordinator就可以容忍它短暂的离线而不开启Rebalance,从而缩短消费者组整体不可用的时间窗口。

为此,社区于2.3和2.4版本引入了静态成员(Static Member)的概念以及一个新的Consumer端参数:group.instance.id。一旦配置了该参数,成员将自动成为静态成员,否则的话和以前一样依然被视为是动态成员。你可以认为这个新参数是一个要被持久化的新member.id。它依然不能由用户指定,构建规则是`group.instsance.id`-UUID。和member.id不同的是,每次成员重启回来后,其静态成员ID值是不变的,因此之前分配给该成员的所有分区也是不变的,而且在没有超时前静态成员重启回来是不会触发Rebalance的。

静态成员Rebalance条件

显然,静态成员触发Rebalance的难度要小于动态成员。如果使用了静态成员,现在触发Rebalance的条件变更为:

  • 新成员加入组:这个条件依然不变。当有新成员加入时肯定会触发Rebalance重新分配分区
  • Leader成员重新加入组:比如主题分配方案发生变更
  • 现有成员离组时间超过了session超时时间:即使它是静态成员,Coordinator也不会无限期地等待它。一旦超过了session超时时间依然会触发Rebalance
  • Coordinator接收到LeaveGroup请求:成员主动通知Coordinator永久离组。毕竟Kafka还是要提供方法让一个成员能够永远地退出组,此时重启Rebalance还是必要的

请求协议变更

为了支持group.instance.id,与消费者组相关的协议格式也要做对应的变化。我看了下官网,JoinGroup、SyncGroup、LeaveGroup和OffsetCommit请求的协议格式都做了相应的变更。比如JoinGroup请求的Request和Response格式都增加了group-instance-id字段,如下所示:

JoinGroup Request (Version: 5) => group_id session_timeout_ms rebalance_timeout_ms member_id group_instance_id protocol_type [protocols]
  group_id => STRING
  session_timeout_ms => INT32
  rebalance_timeout_ms => INT32
  member_id => STRING
  group_instance_id => NULLABLE_STRING
  protocol_type => STRING
  protocols => name metadata
  name => STRING
  metadata => BYTES

JoinGroup Response (Version: 5) => throttle_time_ms error_code generation_id protocol_name leader member_id [members]
  throttle_time_ms => INT32
  error_code => INT16
  generation_id => INT32
  protocol_name => STRING
  leader => STRING
  member_id => STRING
  members => member_id group_instance_id metadata
  member_id => STRING
  group_instance_id => NULLABLE_STRING
  metadata => BYTES

其他请求格式的变更也是类似的,这里就不贴了。

其他变更

鉴于目前静态成员短暂重启或不可用不会触发Rebalance的改动,社区对消费者组最大session过期时间也做了修改。之前Consumer端参数group.min.session.timeout.ms值是6秒——要想在这个时间内重启完一个应用通常都是很困难的,因此社区现在将该值默认值改为30分钟。这就是说,只要配置有静态成员的Consumer程序代码更新及重启在30分钟之内完成,Consumer Group就不会发生Rebalance。当然在这段时间内,该Consumer的消费进度会中断,但是分区分配方案不会发生变化。

总结

目前静态成员的部分功能已经集成进Kafka 2.3版本,还有一部分功能正在开发中,未来会进到2.4版本中。从目前的设计来看,静态成员机制能够帮助我们规避很多线上环境中本不必要的Rebalance,应该说是个很令人期待的新特性。同时,社区针对Rebalance的Stop The World酝酿一次大的修正,即所谓的增量协同式Rebalance(Incremental Cooperative Rebalance)。大致思想是允许单个consumer实例自行采用增量或渐进式的方式进行Rebalance,避免全局的STW。相关的代码正在开发中,后续我也会带来这方面的功能介绍。

Kafka消费者组静态成员(static consumer member)的更多相关文章

  1. 详细解析kafka之 kafka消费者组与重平衡机制

    消费组组(Consumer group)可以说是kafka很有亮点的一个设计.传统的消息引擎处理模型主要有两种,队列模型,和发布-订阅模型. 队列模型:早期消息处理引擎就是按照队列模型设计的,所谓队列 ...

  2. Kafka消费者组再均衡问题

    在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义 ...

  3. Kafka 消费者解析

    一.消费者相关概念 1.1 消费组&消费者 消费者: 消费者从订阅的主题消费消息,消费消息的偏移量保存在Kafka的名字是__consumer_offsets的主题中 消费者还可以将⾃⼰的偏移 ...

  4. Kafka消费组(consumer group)

    一直以来都想写一点关于kafka consumer的东西,特别是关于新版consumer的中文资料很少.最近Kafka社区邮件组已经在讨论是否应该正式使用新版本consumer替换老版本,笔者也觉得时 ...

  5. Kafka设计解析(十三)Kafka消费组(consumer group)

    转载自 huxihx,原文链接 Kafka消费组(consumer group) 一直以来都想写一点关于kafka consumer的东西,特别是关于新版consumer的中文资料很少.最近Kafka ...

  6. kafka消费组、消费者

    consumer group consumer instance 一个消费组可能有一个或者多个消费者.同一个消费组可以订阅一个或者多个主题.主题的某一个分区只能被消费组的某一个消费者消费.那么分区和消 ...

  7. kafka Poll轮询机制与消费者组的重平衡分区策略剖析

    注意本文采用最新版本进行Kafka的内核原理剖析,新版本每一个Consumer通过独立的线程,来管理多个Socket连接,即同时与多个broker通信实现消息的并行读取.这就是新版的技术革新.类似于L ...

  8. kafka 消费组功能验证以及消费者数据重复数据丢失问题说明 3

    原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 背景 上一篇文章记录了kafka的副本机制和容错功能的说明,本篇则主要在上一篇文章的基础上 ...

  9. kafka producer自定义partitioner和consumer多线程

    为了更好的实现负载均衡和消息的顺序性,Kafka Producer可以通过分发策略发送给指定的Partition.Kafka Java客户端有默认的Partitioner,平均的向目标topic的各个 ...

随机推荐

  1. python语言(七)面向对象、异常处理

    一.异常处理 python解释器检测到错误,触发异常(也允许程序员自己触发异常).程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关).如果捕捉成功则进入另外一个处理分 ...

  2. opencv获取网络相机的图像-不用sdk

    海康相机 优点:不用sdk直接网络获取 缺点:速度有1-2秒的延迟 使用型号 1280*680分辨路 #include <iostream> #include<opencv2/ope ...

  3. ORA-25153错误及解决办法

    出现下图错误 原因就是没有临时表空间,所以要建立临时表空间,下面的语句,记得把地址换成你自己想放的地方. alter tablespace temp add tempfile 'C:/temp.dbf ...

  4. ZROI 暑期高端峰会 A班 Day4 生成函数

    一般生成函数 很普及组,不讲了 生成函数是一种形式幂级数,也就是我们只关心系数,不关心未知数具体的值. 比如 \(\sum\limits_{i\ge 0}x^i=\frac{1}{1-x}\).虽然只 ...

  5. UDF——监测指定点的物理量

    Fluent版本:2019 R1 Visual Studio版本:Visual Studio 2013 其他版本应该也是适用的 算例来源于:https://confluence.cornell.edu ...

  6. [BUAA软工]gamma阶段贡献分

    Gamma阶段评分 基本公式:贡献度 = 工作量 × 工作的影响力 × 工作的不可替代性 + 对项目有重大意义的idea gamma阶段所有的工作及idea issue/idea/job (repo: ...

  7. python三级联动

    #以字典的形式 保存相关省市数据 menu={ '北京':{ '朝阳':{ '国贸':{ 'CICC':{}, 'HP':{}, '银行':{}, 'CCTV':{} }, '望京':{ '陌陌':{ ...

  8. SQLServer --------- 将sql脚本文件导入数据库

    创建数据库方法有两种 第一种通过图形化的操作界面 第二种通过 sql 语句 sql server 如何执行.sql 文件,的原理就是执行sql语句进行创建 打开数据库后找到   最左侧文件 找到需要执 ...

  9. unity内存管理(转)

    转自:https://www.cnblogs.com/zsb517/p/5724908.html Unity3D 里有两种动态加载机制:一个是Resources.Load,另外一个通过AssetBun ...

  10. Java学习:集合的使用与数组的区别

    ArrayList 集合 ArrayList 集合 ArrayList<String> list = new ArrayList<>(); 对于ArrayList来说,有一个尖 ...