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. (HK1-1)海康网络摄像机的使用

    https://blog.csdn.net/u014552102/article/details/86700057 一.手机客户端操作:  首先在莹石商城官网https://www.ys7.com/下 ...

  2. day 19

    If you think you can, you can. And if you think you can't, you're right.

  3. PATA1001A+BFormat

    这里学到的主要是将数字存储到数组中,倒序输出使用取余10加除10 while(sum) { num[len++] = sum % 10; sum /= 10; } 然后是每三位输出一个逗号,因为是倒序 ...

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

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

  5. 区间dp提升复习

    区间\(dp\)提升复习 不得不说这波题真的不简单... 技巧总结: 1.有时候转移可以利用背包累和 2.如果遇到类似区间添加限制的题可以直接把限制扔在区间上,每次只考虑\([l,r]\)被\([i, ...

  6. 网络测试工具netperf(转)

    http://pangyi.github.io/blog/20141210/wang-luo-ce-shi-gong-ju-netperf/ 网络测试工具netperf 2014年12月10日 一般我 ...

  7. Hibernate通过createSQLQuery( )方法实现增删改查

    一.项目结构 二.hibernate核心配置文件:   hibernate.cfg.xm <?xml version="1.0" encoding="UTF-8&q ...

  8. 收藏:png8和png24的根本区别

    1.png8和png24的根本区别,不是颜色位的区别,而是存储方式不同. 2.png8有1位的布尔透明通道(要么完全透明,要么完全不透明),png24则有8位(256阶)的布尔透明通道(所谓半透明). ...

  9. Python OpenCV4获取轮廓最大内切圆和外接圆

    为了方便讲解,我们先来创建一个多边形做演示 第一步:创建图像,并绘制一个六边形.代码和生成图像如下: # Create an image r = 100 src = np.zeros((4*r, 4* ...

  10. .NetCore快速搭建ELK分布式日志中心

    懒人必备:.NetCore快速搭建ELK分布式日志中心   该篇内容由个人博客点击跳转同步更新!转载请注明出处! 前言 ELK是什么 它是一个分布式日志解决方案,是Logstash.Elastaics ...