消息队列(三)如何保证消息不被重复消费?
一、为什么会出现重复消费的问题?
RabbitMQ、RocketMQ、Kafka都有可能出现重复消费的问题,导致重复消费的原因可能出现在生产者,也可能出现在 MQ 或 消费者。这里说的重复消费问题是指同一个数据被执行了两次,不单单指 MQ 中一条消息被消费了两次,也可能是 MQ 中存在两条一模一样的消费。
生产者:生产者可能会重复推送一条数据到 MQ 中,为什么会出现这种情况呢?也许是一个 Controller 接口被重复调用了 2 次,没有做接口幂等性导致的;也可能是推送消息到 MQ 时响应比较慢,生产者的重试机制导致再次推送了一次消息。
MQ:在消费者消费完一条数据响应 ack 信号消费成功时,MQ 突然挂了,导致 MQ 以为消费者还未消费该条数据,MQ 恢复后再次推送了该条消息,导致了重复消费。
消费者:消费者已经消费完了一条消息,正准备但是还未给 MQ 发送 ack 信号时,此时消费者挂了,服务重启后 MQ 以为消费者还没有消费该消息,再次推送了该条消息。
二、如何保证消息队列的幂等性?
消息的重复消费问题实际上涉及到消息者消费消息的幂等性问题。重复消费问题通常在消费者端解决,当然生产者端也最好简单控制下不要生产重复数据,但是一般情况下 MQ 是允许存在多条一样的数据的,只是消费端就不允许消费两条一样的数据,所以幂等性保障通常都是在消费者端实现。
那么消费者怎么解决重复消费问题呢?这个问题解决起来也比较简单,这里提供两种方法
状态判断法:消费者消费数据后把消费数据记录在 redis 中,下次消费时先到 redis 中查看是否存在该消息,存在则表示消息已经消费过,直接丢弃消息。
业务判断法:通常数据消费后都需要插入到数据库中,使用数据库的唯一性约束防止重复消费。每次消费直接尝试插入数据,如果提示唯一性字段重复,则直接丢失消息。一般都是通过这个业务判断的方法就可以简单高效地避免消息的重复处理了。
三、总结
本文描述了为什么会出现重复消费的问题,生产者、MQ、消费者都有可能导致消息的重复消费。重复消费问题通常是在消费者端解决,而我们一般默认 MQ 中是有可能存在两条一模一样的数据的,消费者要做幂等性处理。而幂等性处理最简单高效的处理是插表时根据唯一性字段判断,如订单号等。
评论