转发请注明原创地址:https://www.cnblogs.com/dongxiao-yang/p/10602799.html

某日晚高峰忽然集群某个大流量业务收到lag报警,查看客户端日志发现reblance一直无法成功,日志如下

根据客户端日志显示consumer在尝试joingroup的过程中收到了服务端COORDINATOR状态不正常的信息,怀疑是服务端负责这个consumer-group的broker在coordinator元信息管理上出现了问题。

于是跑到对应的节点上看一下server日志,发现在一台刚才有过重启的服务节点上产生如下日志

Failed to append 363 tombstones to __consumer_offsets-38 for expired/deleted offsets and/or metadata for group consumer-group. (kafka.coordinator.GroupMetadataManager)
org.apache.kafka.common.errors.NotLeaderForPartitionException: Leader not local for partition __consumer_offsets-38 on broker 。

怀疑是这个服务重启的过程中__consumer_offset分区有部分数据或者文件有异常导致coordinator无法提供服务导致,停掉有问题节点后发现客户端reblance很快就成功了,于是怀疑问题节点产生了坏文件,后续删除对应分区可以重启成功服务,但是对应group的业务又开始报错

20 Mar 2019 15:31:32,000 INFO  [PollableSourceRunner-KafkaSource-bl_app_event_detail_source] (org.apache.kafka.clients.consumer.internals.ConsumerCoordinator$OffsetCommitResponseHandler.handle:542)  - Offset commit for group consumer-group failed due to NOT_COORDINATOR_FOR_GROUP, will find new coordinator and retry
20 Mar 2019 15:31:32,001 INFO  [PollableSourceRunner-KafkaSource-bl_app_event_detail_source] (org.apache.kafka.clients.consumer.internals.AbstractCoordinator.coordinatorDead:529)  - Marking the coordinator 2147483543 dead.

kafka 自从0.9以来摒弃了consumer把offset存在zk的做法而是都存到了__consumer_offsets这个系统topic里面,同时consumer端的reblance都是依靠server端的coordinator负责调度协调。至于每个group怎么选择对应broker节点是根据下面这个简单的hashcode对__consumer_offsets分区数取模的算法得出来的,

  def partitionFor(groupId: String): Int = Utils.abs(groupId.hashCode) % groupMetadataTopicPartitionCount

所以看上去是重启节点拉起来后客户端发现对应的offset分区leader又活了,但是活过来的leader却告知客户端NOT_COORDINATOR_FOR_GROUP这个矛盾。但是明明有问题的offset文件已经被手动删除掉了,重新拉副本也成功了,为什么还是会有join group不成功的现象呢。

继续查看问题节点,发现问题节点在Loading group metadata for之类的日志的时候一直没有输出对应的问题group相关日志,初步判断broker重启过程中load group信息的时效出了问题。

  def onLeadershipChange(updatedLeaders: Iterable[Partition], updatedFollowers: Iterable[Partition]) {
// for each new leader or follower, call coordinator to handle consumer group migration.
// this callback is invoked under the replica state change lock to ensure proper order of
// leadership changes
updatedLeaders.foreach { partition =>
if (partition.topic == GROUP_METADATA_TOPIC_NAME)
groupCoordinator.handleGroupImmigration(partition.partitionId)
else if (partition.topic == TRANSACTION_STATE_TOPIC_NAME)
txnCoordinator.handleTxnImmigration(partition.partitionId, partition.getLeaderEpoch)
}

如上述代码所示,kafka在offset分区重新被选举为leader的时候才会去加载对应的group信息,而且所有新leader是foreach单线程循环,如果其中有一个慢的剩下的group都会受到影响。查看问题节点果然除了被删掉的offset分区还有一个分区offset历史文件很多,多到500G的体量,这和offset这种只保存最新数据的场景明显是不符合的,这个大小会导致服务端加载offset信息长到无法接受的程度。

为了尽快回复offset元信息,把问题节点的offset partition全都重新分配到其他节点,在重分配的过程中发现新的副本会不断的删除同步过来的过期数据最后结束后整个分区的大小只有几十M,于是坚定了原来分区大小不正常的判断 。对于__consumer_offsets这种compact策略的topic,kafka内部是有一个专门的logcleaner线程负责日志的合并,但是刚开始出问题的节点经过了几次重启,原始的现场早已不存在,于是把整个集群每个服务挨个查了一遍,果然在另一台看似正常的机器上同样发现了一个很大的offset分区,jstack了一下,发现kafka-log-cleaner-thread这个线程已经没了!重启该服务后发现问题分区的日志也开始正常删除。可惜的是由于服务日志只保留了最近7天的,kafka-log-cleaner-thread的错误日志已经找不到了,这个有待后续复现确认。

回顾了一下处理问题过程中出现的其他现象,其实都是有提示的,像是关掉问题节点的时候server日志会报

WARN Map failed (kafka.utils.CoreUtils$)
java.io.IOException: Map failed
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:940)
at kafka.log.AbstractIndex$$anonfun$resize$1.apply(AbstractIndex.scala:111)
at kafka.log.AbstractIndex$$anonfun$resize$1.apply(AbstractIndex.scala:101)

以及kafka jvm第一次崩掉的hs_err_pid日志会提示内存不足

Native memory allocation (mmap) failed to map 65536 bytes for committing reserved memory

由于kafka使用的mmap方式映射了数据文件以及索引,这个mmap failed就已经提示了文件过多。

结论:kafka的offset数据每个group会根据hash取模的方式发到一个固定的_consumer_offsets分区中,_consumer_offsets分区的leader负责对应groupid的coordinator服务,_consumer_offsets

的删除是由kafka-log-cleaner-thread执行的,这个线程个数默认是1,如果线程崩掉了offset历史分区文件会一直无法删除,导致jvm崩掉并且服务恢复的时候group元信息长时间的无法加载导致reblacne报错。

记一次kafka客户端NOT_COORDINATOR_FOR_GROUP处理过程的更多相关文章

  1. 【原创】大叔问题定位分享(5)Kafka客户端报错SocketException: Too many open files 打开的文件过多

    kafka0.8.1 一 问题 10月22号应用系统忽然报错: [2014/12/22 11:52:32.738]java.net.SocketException: 打开的文件过多 [2014/12/ ...

  2. python confluent kafka客户端配置kerberos认证

    kafka的认证方式一般有如下3种: 1. SASL/GSSAPI  从版本0.9.0.0开始支持 2. SASL/PLAIN   从版本0.10.0.0开始支持 3. SASL/SCRAM-SHA- ...

  3. 解Bug之路-记一次存储故障的排查过程

    解Bug之路-记一次存储故障的排查过程 高可用真是一丝细节都不得马虎.平时跑的好好的系统,在相应硬件出现故障时就会引发出潜在的Bug.偏偏这些故障在应用层的表现稀奇古怪,很难让人联想到是硬件出了问题, ...

  4. Kafka客户端内存缓冲GC处理机制--客户端内存

    1.Kafka的客户端缓冲机制 首先,先得给大家明确一个事情,那就是在客户端发送消息给kafka服务器的时候,一定是有一个内存缓冲机制的. 也就是说,消息会先写入一个内存缓冲中,然后多条消息组成了一个 ...

  5. SpringMVC处理客户端请求的过程

    SpringMVC处理客户端请求的过程 以程序部署在Tomcat上为例,网站程序使用SpringMVC框架开发. 1.客户端发起一个访问网站的请求(如: localhost:8080/index). ...

  6. Kafka 客户端实现逻辑分析

    这里主要分析kafka 客户端实现 (代码分析以perl kafka实现为准) kafka客户端分为生产者和消费者,生产者发送消息,消费者获取消息. 在kafka协议里客户端通信中用到的最多的四个协议 ...

  7. 转:Kafka 客户端TimeoutException问题之坑

    原文出自:http://www.jianshu.com/p/2db7abddb9e6 各种TimeoutException问题 会抛出org.apache.kafka.common.errors.Ti ...

  8. Erlang 编写 Kafka 客户端之最简单入门

    Erlang 编写 Kafka 客户端之最简单入门 费劲周折,终于测通了 erlang 向kafka 发送消息,使用了ekaf 库,参考: An advanced but simple to use, ...

  9. kafka客户端发布record(消息)

    kafka客户端发布record(消息)到kafka集群. 新的生产者是线程安全的,在线程之间共享单个生产者实例,通常单例比多个实例要快. 一个简单的例子,使用producer发送一个有序的key/v ...

随机推荐

  1. Dependent Parameters in Concurrent Program using Special Value Set

    Dependent Parameters in Oracle Applications Requirement: Say there is a concurrent program that lets ...

  2. C#之Enum中的Flag

    我们知道在默认情况下,第一个枚举数的值为0,后面每个枚举数的值一次加1. enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri}; 我们也可以用初始值来重写默认值. ...

  3. LaTeX 的对参考文献的处理

      LaTeX 的对参考文献的处理实在是非常的方便,我用过几次,有些体会,写出来供大家 参考.当然,自己的功力还不够深,有些地方问题一解决就罢手了,没有细究.     LaTeX 对参考文献的处理有这 ...

  4. iOS: 常用的宏

    Github地址:https://github.com/XFZLDXF/Macro/blob/master/MacroDefinition.h // // MacroDefinition.h // M ...

  5. SQL 备份还原单个表

    如果只想备份或恢复单个表而不想备份或恢复整个数据库的话,往往有以下方法: 1.在Sql server2000 中可以使用DTS来将该表的数据导出成另外的文件格式.当需要恢复时,可以将该文件中数据再通过 ...

  6. 784 - Maze Exploration

    #include <stdio.h> #include <string.h> char maze[50][100]; void search(int i,int j) { if ...

  7. Struts2的动态Action实现

    源自:Struts2的动态Action实现 在Struts2中动态方法调用有三种方式. 一.指定method属性在struts.xml中指定action的method属性. <package n ...

  8. ActiveMQ订阅模式持久化实现

    实现步骤:1.配置发送xml,applicationContext-send.xml <?xml version="1.0" encoding="UTF-8&quo ...

  9. Linux内核project导论——linux学习和职业曲线(刚開始学习的人,中级,高级都可參考)

    Linux世界介绍 给自己定级 门外汉: 不会安装操作系统 不会用虚拟机(安装和使用) 入门级: 熟悉常见的发行版,甚至装过而且能用一些特殊发行版(比如kali)做过一些简单的图形界面的使用. 会一些 ...

  10. Expression-Based Access Control

    Expression-Based Access Control Spring Security 3.0 introduced the ability to use Spring EL expressi ...