Pulsar重发的消费可以避免吗?
2023-12-14 05:59:50
重发消费问题定位
在 2022 年初的一个晚上,同事突然给我发消息:他有一个业务场景,使用 Pulsar 进行消息消费的时候会重复消费,已经对 Pulsar 的各个参数都检查了一遍,而且业务上保证了消息被消费之后一定会 ACK 成功,这种情况应该不会发生重复消费。根据同事的,我初步断定:是业务代码在消费的时候出现问题了。因为 Pulsar 本身是通过 ACK 机制来保证消息只会被消费一次的,如果出现了重发消费的情况,只可能是 ACK 失败了。
于是,我让同事给我发来了他的消费代码,我大致看了看,发现了一个问题:他的业务代码是通过定期消费消息的方式来消费的,定期消费的方式有一个特点,就是当消费任务运行的时候,它是会一直不停地消费消息的,直到没有新的消息到来的时候才会停止,然后在一段时间之后才会继续消费,如此反复。这种情况下,如果出现消费失败的情况,很有可能就会导致消费任务一直不停地重复消费。
我让同事确认一下,他的业务场景里面是否会出现消费失败的情况,同事告诉我:业务场景里面不会出现消费失败的情况,每一条消息都会被消费成功。于是我就猜想,是不是 ACK 机制出现了问题?于是我让同事把 ACK 逻辑也发给我看一看。
查看 ACK 逻辑后,我发现 ACK 的逻辑也没有问题,通过 debug 查看了一下 ACK 的执行情况,也确实是每次都会执行 ACK。那么问题出在哪里呢?
于是我让同事查一下 Pulsar 的官方文档,看一看关于重复消费的问题,是否有一些解释。果然,在 Pulsar 的官方文档中,有这样一段话:
Pulsar 通过 ACK 机制来保证消息只会被消费一次,但是如果 ACK 失败,那么消息就会被重新发送。
我让同事再查一下,Pulsar 的 ACK 机制是如何工作的,在官方文档中,有这样一段话:
Pulsar 的 ACK 机制是通过将消费者的消费进度记录在一个叫作「消费进度日志」的文件中。当消费者消费完一条消息之后,它会将这条消息的 ID 记录到消费进度日志中。当消费者崩溃或者重启的时候,它会从消费进度日志中恢复消费进度,并从上一次记录的消费位置开始消费。
至此,我终于找到了问题的所在:由于 Pulsar 的 ACK 机制是通过将消费进度记录在一个叫作「消费进度日志」的文件中,如果这个文件被删除了,那么消费者的消费进度就会丢失,从而导致消费者从头开始消费,这就是为什么会出现重复消费的原因。
于是我让同事检查一下,他是否在使用 Pulsar 的过程中删除了消费进度日志文件。同事告诉我说:没有删除过消费进度日志文件。我说:可能是你的业务代码在消费的时候,不小心把消费进度日志文件给删了。同事说:不可能,我的业务代码里面没有删除消费进度日志文件的逻辑。
我让同事把他的业务代码再发给我看一遍,我仔细地检查了一遍,发现他的业务代码里面确实没有删除消费进度日志文件的逻辑。但是,我仍然不相信他的话,因为我知道,有的时候,即使业务代码里面没有删除消费进度日志文件的逻辑,也可能会导致消费进度日志文件被删除。
于是我让同事再查一下,他的服务器上是否安装了某种软件,这种软件会自动删除消费进度日志文件。同事告诉我:他服务器上安装了一个叫作「文件清理工具」的软件,这个软件会自动删除服务器上超过一定时间的日志文件。我说:那就是这个软件把你的消费进度日志文件给删了。
同事恍然大悟,他说:对,我之前确实安装了一个叫作「文件清理工具」的软件,这个软件会自动删除服务器上超过一定时间的日志文件,我把它卸载了,看看还会不会出现重复消费的情况。
卸载了「文件清理工具」软件之后,重复消费的情况果然消失了。
避免重发消费的建议
为了避免出现重复消费的情况,我们可以采取以下措施:
- 使用顺序消费。顺序消费可以保证消息被按照顺序消费,如果出现消费失败的情况,可以从失败的消息开始重新消费,而不会重复消费前面的消息。
- 使用 At-least-once 消费策略。At-least-once 消费策略可以保证消息被消费至少一次,但是可能会重复消费消息。
- 使用 Exactly-once 消费策略。Exactly-once 消费策略可以保证消息被消费一次,但是实现起来比较复杂。
- 不要在业务代码中删除消费进度日志文件。消费进度日志文件是 Pulsar 用来记录消费者消费进度的文件,如果这个文件被删除了,那么消费者会从头开始消费,从而导致重复消费。
- 不要在服务器上安装会自动删除日志文件的软件。有些软件会自动删除服务器上超过一定时间的日志文件,如果这些软件不小心把消费进度日志文件给删了,那么也会导致重复消费。