RabbitMQ如何保证消息不被重复消费

前言:

正常情况下,消费者在消费消息后,会给消息队列发送一个确认,消息队列接收后就知道消息已经被成功消费了,然后就从队列中删除该消息,也就不会将该消息再发送给其他消费者了。不同消息队列发出的确认消息形式不同,RabbitMQ是通过发送一个ACK确认消息。但是因为网络故障,消费者发出的确认并没有传到消息队列,导致消息队列不知道该消息已经被消费,然后就再次消息发送给了其他消费者,从而造成重复消费的情况。

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性。假设你有个系统,消费一条往数据库里插入一条,要是你一个消息重复两次,不就插入了两条,这数据就错了。但是你要是消费到第二次的时候,自己判断一下已经消费过了,直接扔了,不就只保留了一条数据。

幂等性,通俗点说,就一个数据,或一个请求,给你重复来多次,你得确保对应的数据不会改变,不能出错。

重复消费问题的解决思路是:保证消息的唯一性,即使多次传输,也不让消息的多次消费带来影响,也就是保证消息等幂性;幂等性指一个操作执行任意多次所产生的影响均与一次执行的影响相同

具体解决方案如下:

(1)改造业务逻辑,使得在重复消费时也不影响最终的结果。例如对SQL语句: update t1 set money = 150 where id = 1 and money = 100; 做了个前置条件判断,即 money = 100 的情况下才会做更新,更通用的是做个 version 即版本号控制,对比消息中的版本号和数据库中的版本号。

(2)基于数据库的的唯一主键进行约束。消费完消息之后,到数据库中做一个 insert 操作,如果出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

(3)通过记录关键的key,当重复消息过来时,先判断下这个key是否已经被处理过了,如果没处理再进行下一步。. 生产者发送每条数据的时候,里面添加一个全局唯一的id, 消费者消费到消息后, 先根据消息id去redis中查询,如果redis不存在,就处理消息,然后将消息id写入redis。如果redis中存在,说明消息已经消费过,就不用处理。

① 通过数据库:比如处理订单时,记录订单ID,在消费前,去数据库中进行查询该记录是否存在,如果存在则直接返回。

② 使用全局唯一ID,再配合第三组主键做消费记录,比如使用 redis 的 set 结构,生产者发送消息时给消息分配一个全局ID,在每次消费者开始消费前,先去redis中查询有没有消费记录,如果消费过则不进行处理,如果没消费过,则进行处理,消费完之后,就将这个ID以k-v的形式存入redis中(过期时间根据具体情况设置)。

相关推荐
_waylau6 小时前
鸿蒙架构师修炼之道-面向对象的分布式架构
分布式·华为·架构·架构师·harmonyos·鸿蒙
Francek Chen8 小时前
【大数据存储与管理】NoSQL数据库:03 NoSQL与关系数据库的比较
大数据·数据库·分布式·nosql
FeBaby10 小时前
Java 高并发场景下 Redis 分布式锁(UUID+Lua)最佳实践
java·redis·分布式
richard_yuu12 小时前
工控场景落地|分布式协调与动态重配置管理,如何实现产线不停机升级?
分布式
MoFe113 小时前
【.net core】【RabbitMq】rabbitmq在.net core中的简单使用
分布式·rabbitmq·.netcore
何中应13 小时前
在windows本地部署RabbitMQ
分布式·消息队列·rabbitmq
Wild API13 小时前
按任务轻重做模型分流的实战思路
分布式·微服务·架构
低客的黑调14 小时前
RabbitMQ-从入门到生产落地
分布式·rabbitmq
宸津-代码粉碎机14 小时前
Spring Boot 4.0虚拟线程实战续更预告:高阶技巧、监控排查与分布式场景落地指南
java·大数据·spring boot·分布式·后端·python
霖霖总总1 天前
[Redis小技巧32]Redis分布式锁的至暗时刻:从原理演进到时钟跳跃的终极博弈
数据库·redis·分布式