其实文章名可以叫 记一次线上 Kafka 问题排查,但觉得稀松平常,弄些术词显得硬核点,hhh,言归正传,线上一个 Go 服务是一组 Kafka 的消费者,在运行了很多天之后,Kafka 数据突然积压了,查看 Kafka 服务正常,查看 Go 服务也运行正常,进到 Go 服务容器内部查看日志,发现消费者的 Go 服务频繁 rebalance 并且大概率返回失败。因为我们所使用的 Kafka 分配的分区为3,所以 Go 服务 一个 Deployment 三个 Pod。
错误信息如下:

kafka server: The provided member is not known in the current generation
Request was for a topic or partition that does not exist on this broker

有时候日志里还会伴随着

i/o timeout

甚至还触发了 阿里云的 K8s 运营报警,在代码中加了 errors 和 notifications 日志,发现每次错误都伴随着 rebalance。

一开始以为是超时时间过短导致,调大了连接超时和读写超时的时间,但是问题没有得到解决。

又以为是我们 Go 服务处理业务逻辑问题,性能不足,导致处理时间过长,以至于 Kafka Server 认为 Client 死掉了,然后进行rebalance 导致的,于是将每条获取到的 message 放到 channel 中,弄了一个线程池去消费 channel 来解决问题,但是问题仍然没有解决。 阅读了 sarama 的 heartbeat 机制,发现每个 consumer 都有单独的 goroutine 每 3 秒发送一次心跳。因此这个处理时间应该只会导致消费速度下降,不会导致 rebalance。

于是只好另外启动了一个消费者,指定了另外一个 group id,来做测试,在消费过程中,发现并未发生 rebalance。这时我就裂开了,懵逼了。得亏我最近查文档技能点满了,终于翻到一篇文章看到了这个问题的解决方案,全文英语,我就不贴出来了,简单概括讲下:

Kafka 不同 Topic 的 consumer 如果用的 groupId 名字一样的情况下,其中任意一个 Topic 的 consumer 重新上下线都会造成剩余所有的 consumer 产生 reblance 行为,即使大家不是同一个 Topic,这主要是由于 Kafka 官方支持一个 consumer 同时消费多个 Topic 的情况,所以在 zk 上一个 consumer 出现问题后,zk 是直接把 group 下所有 consumer 都通知一遍,这个与以前观念里认为 group 从属于某一个 Topic 的概念完全不同。

而我之前出问题的 Go 服务所使用的的消费组 groupId 在不同 Topic 下都有多个消费者。为了验证这个问题,我修改了我的消费组的 groupId,加了后缀与其它消费者区分开,果然之前频繁 reblance 再也没有发生过 reblance了。

最后修改:2020 年 06 月 09 日 12 : 41 PM
如果觉得我的文章对你有用,请随意赞赏