Kafka消费端怎样保证消息不丢失?真正的处理方式其他文章中竟然没有见过_重启

现有方案

之前kafka消费端采用的是standalone模式,使用的是单分片。消费端指定分片0。使用了zookeeper进行选主。只在主节点进行消费。这个选主的过程要几分钟才能选出来。之后为了保证消息不丢失,已经执行完的kafkaOffset会保存到数据库。每次在重启时会拉取数据库里的kafkaOffset执行consumer.seek到此位置。然后才开始consumer.poll.

我们做过破坏性测试:在测试环境有2台服务器主备时,如果把两台服务器都完全杀死再启动起来。kafka消费端就无法正常消费了。原因是

Kafka log清理策略配置log.retention.minutes=10

默认kafka只保留10分钟的log。而将两个POD完全停掉再启动,因为加上较长的选主时间,所以数据库中存的kafkaOffset数据过期被清理了。造成大量invalid message。这会造成消费端的iterator损坏,致使消费进程挂掉。

这时候需要人工处理,将数据库的数据清理掉,重启服务。清理掉之后,策略是从最新的开始消费。所以历史的数据都会丢失。

改进方案

对于同一个分片,如果只是consumer.subscribe某个主题,根据咱们【编程一生用户交流群】朋友的说法,多个消费端只有一个在消费,其他节点在故障转移时才会进行消费。理论上这种Kakfa默认的故障转移策略要比zookeeper选主要快。具体我没有测试。

我使用的是consumer.assign指定分片。这两个两个节点都会进行消费。为了不增加kafka流量负担。我使用了带过期时间的分布式锁,只有抢到锁才会进行消费。使用手动提交offset代替自动提交offset。异步多线程执行加速,但是需要等结果返回再提交offset,这样就可以保证消息不丢失。

一旦一台服务宕机,另外一台可以快速接管。就算两台都宕机,重启后可以毫秒级别继续消费。这样,只需要将

Kafka log清理策略配置log.retention.minutes=10

调大一些,比如保留1小时的,服务可以1小时内恢复,那就不会丢失消息。

其他事项

服务启动时可以设置consumer.seek从lastest还是earliest的offset开始,但是还是建议consumer.seekToEnd或者consumer.seekToBegin来明确。如果不想丢消息,还是建议除了第一次启动之后,其他情况直接consumer.poll。

consumer.assigment可以获取到当前consumer的分片,这个在多分片下进行监控会有用。但是这个只有在第一次执行consumer.poll之后才能获取到值。所以程序启动时获取到为空是正常现象。