问题描述

  最近线上的一个数据服务(服务B)出现了一个比较诡异的问题 ,该服务消费上游服务(服务A)产生的kafka消息数据,上线后一直运行平稳,最近一周在两次上线的时候出现了大量数据更新的情况,查看服务日志发现每次都从kafka的起始位置进行消费了,而kafka topic的数据一般是保留最近7天,但是这是不应该发生的,因为监控显示服务B在重启前是没有什么延迟的,更加不可能说是有7天的延迟,而且在kafka的broker上理论上会保留每个consumer-group的信息,一般会有一定的保留时长,在业务B重启后会接着原来提交的offset点位进行消费。

问题排查

  那为什么会出现这种问题呢,仔细排查服务B的启动日志,确实在启动后将offset放到了topic的earlist位置,然后排查kafka topic中的数据,发现有几天没有更新了,再往上追溯,原来服务A因为数据平台的一些故障停止运行了几天(新上的分布式计算任务,未来得及做全面的监控)。那这又为什么会导致B服务重启后从头消费呢。

  直观感觉上应该是kafka的broker对consumer group的管理机制导致的,因为一直没有新的数据产生,所以当服务B消费了topic末尾的时候不会再提交新的offset(服务B使用的是手动提交的模式),只是有维持session 的心跳,这个时候broker端不会更新consumer-group的信息,所以consumer-group的有效时间在超过max.poll.interval.ms之后,server端会将对应的consumer踢除掉,在这个consumer-group里面没有任何consumer之后,在经历offsets.retention.minutes之后,kafka-server就会将相关的consumer-group的消费数据清理掉。在这之后,如果服务B对应的consumer进行重启的话,如果设置的auto_offset_reset=earliest的话就会导致数据消费从头开始。

回过头来看线上服务B当时的表现也不是每次都从头消费,两次从头消费都是超过了两天的上线(因为没有新的数据进来,所以等于有将近48h没有进行commit),仔细查找了一下kafka的相关设置,看到这个

offsets.retention.minutes: 默认值为1440, 是24h,
After a consumer group loses all its consumers (i.e. becomes empty) its offsets will be kept for this retention period before getting discarded.
For standalone consumers (using manual assignment), offsets will be expired after the time of last commit plus this retention period.

解决方案

  1. 手动提交的代码调整,在每次poll下来之后不管是否有数据都进行一次手动的commit操作
  2. 在少量重复消费不影响服务的情况下可以尝试使用自动提交

需要注意的时候,有时候测试环境确实可能出现长时间没有数据的情况,这个时候也可以在kafka server端增大offsets.retention.minutes的设置。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐