从一个生产上的错误看kafka的消费再均衡问题
问题描述
项目在生产上的一段错误日志如下,
这是一段kafka的错误日志,大概的意思是说,
kafka的服务端在超过了 max.poll.interval.ms 时间内没有收到某个消费者的心跳,认为该消费者已经“挂了”,所以进行了topic的分区所有权“再均衡”。
问题的分析
按照我的个人习惯,遇到类似这样的生产问题,解决之后我会思考下涉及的技术细节并做整理。
如果对问题涉及的技术细节非常的了解,对于定位问题是非常有帮助的。本文就带你深入了解下上面那个错误日志涉及的一些技术细节。
kafka的topic分区
为了提高消息处理的高可用以及便于横向扩展,kafka引入了topic的分区概念。属于同一个消费者群组的消费者可以分担的消费同一个topic不同分区的消息。从而达到分流的作用,可以使消息处理更高效。
如上图示例所示,topic A有三个分区,同时我们有三个属于同一个群组的消费者,这样每个消费者可以负责消费一个分区。大家各自负责自己的分区,系统有条不紊的运行着。
一般情况下,我们通过增加群组里的消费者数量来提高 kafka 的消费能力。不过要注意,不要让消费者的数量超过主题分区的数量,多余的消费者只会被闲置。
心跳机制
kafka 的服务端需要一直监控有哪些消费者在消费,监控的机制是通过消费者不断的发送心跳包实现的。消费者发送心跳有两个途径,一个是轮询(poll,这里不是为了秀英文,注意联系上面的错误日志),一个是消费后提交 offset 。
这两种方式是两个独立的线程,互相不干扰。
只要消费者以正常的时间间隔发送心跳,就被认为是活跃的,说明它还在读取分区里的消息,否则就被认为是已经“死亡”。 这个所谓的正常的时间间隔,就是不能超过 max.poll.interval.ms。
kafka的分区再均衡
消费者通过向服务端发送心跳来维持它们和群组的从属关系以及它们对分区的所有权关系。如果服务端认为某个消费者已经“死亡”,就会触发一次再均衡。如下图所示,
前面说过,群组里的消费者共同读取主题的分区。
比如有一个新的消费者加入群组,它读取的是原本由其他消费者读取的消息。当一个消费者被关闭或发生崩溃时,它就离开群组,原本由它读取的分区将由群组里的其他消费者来读取。
分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为再均衡。
再均衡有什么意义吗?
当然,有了再均衡,我们可以放心的添加或者移除某个消费者,而不用担心消息的丢失。
解决问题
了解了相关的技术细节后,我们可以顺藤摸瓜,慢慢排查问题。基于前面的分析,我给出几个排查的方向:
- 看看某个消费者的服务是否已经挂了?
- 如果服务正常运行,服务所在的节点是否存在内存或者CPU占满的情况,导致消费者无法及时的发送心跳等。我遇到的情况就是后者引起的。后来解决了内存占用满的问题后,kafka的错误就不存在了。
- 根据自己实际的业务情况,考虑增加 max.poll.interval.ms 的值。
参考:
- 《kafka权威指南》
- linux学习第四十六篇:Nginx防盗链,Nginx访问控制,Nginx解析php相关配置,Nginx代理
- linux学习第四十七篇:Nginx负载均衡,ssl原理,生产ssl密钥对,Nginx配置ssl
- linux学习第四十八篇:php-fpm的pool,php-fpm慢执行日志,定义open_basedir,php-fpm进程管理
- linux学习第五十一篇:NFS介绍,NFS服务端安装配置,NFS配置选项
- linux学习第五十二篇: exportfs命令,NFS客户端问题,FTP介绍,使用vsftpd搭建ftp服务
- linux学习第五十四篇:Tomcat介绍,安装jdk,安装Tomcat
- linux学习第五十九篇:LVS DR模式搭建,keepalived lvs
- linux学习第五十四篇:配置Tomcat监听80端口,配置Tomcat的虚拟主机,Tomcat日志
- linux学习第五十六篇:集群介绍,keepalived介绍,用keepalived配置高可用集群
- linux学习第五十八篇: 负载均衡集群介绍,LVS介绍,LVS的调度算法,LVS NAT模式搭建
- Python中eval带来的潜在风险,你知道吗?
- React Native自定义导航条
- android混淆那些坑
- 微信小程序开发入门篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法