RabbitMQ消费端幂等性概念及解决方案
本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
-
🚀 魔都架构师 | 全网30W技术追随者 -
🔧 大厂分布式系统/数据中台实战专家 -
🏆 主导交易系统百万级流量调优 & 车联网平台架构 -
🧠 AIGC应用开发先行者 | 区块链落地实践者 -
🌍 以技术驱动创新,我们的征途是改变世界! -
👉 实战干货:编程严选网
1 幂等性问题由来
1.1 消费成功,但未通知MQ消费成功
虽有确保消息成功消费的ACK机制,但因网络或服务问题,Consumer可能无法成功调用ack来通知broker消息已被成功消费。此时,broker认为我们未消费成功,又再次推送消息,导致重复消费。
因此,现实需考虑消息幂等问题,尤其涉及敏感操作如消费微信支付通知。
1.2 vx多次投递消息给Producer
微信可能多次向Producer发消息,这也可能导致消息幂等问题。
2 啥是幂等性?
用户对同一操作发起的一次请求或多次请求的结果一致。
如数据库乐观锁,执行更新操作前:
-
先去数据库查询version -
再执行更新语句 -
以version作条件,若执行更新时有其他人先更新该表的数据,那这条件就不生效,也就不会执行操作
通过乐观锁保障幂等性。
3 Con幂等性
3.1 定义
消费端实现幂等性,即消息永远不会消费多次,即使收到重复消息。
业务高峰期最易产生重复消费问题:当Con消费完消息后给Pro返回ack时,由于网络中断,导致Pro未收到确认信息,该消息就会被重新发送并被Con消费,但实际上该Consumer已成功消费,导致重复消费。
3.2 幂等性方案
3.2.1 唯一ID+指纹码
生成全局唯一messageld。利用DB主键去重:
SELECT COUNT(1) FROM T_ORDER
WHERE ID = 唯一ID
AND IS_CONSUM = 指纹码
唯一ID:业务表的主键
指纹码:为区别每次正常操作的码,每次操作时生成指纹码。可用:
-
时间戳+业务编号 -
或标志位
优势
实现简单。
弊端
高并发下有数据库写入的性能瓶颈。
解决方案
根据ID进行分库分表算法路由。
小结
先根据消息生成一个全局唯一ID,再加个指纹码。指纹码不一定是系统生成,而是一些外部规则或内部业务规则去拼接,就是为保障这次操作的绝对唯一性。
ID + 指纹码拼接好的值作为数据库主键,即可去重。消费消息前,先去数据库查询这条消息的指纹码标识:
-
不存在,就执行insert -
存在,代表已被消费,就不需要管了
3.2.2 利用Redis原子性
需考虑是否落库:
-
落,数据库和缓存咋做到原子性
-
不落,都存储到缓存,咋设置定时同步策略
这里只提Redis原子性去解决MQ幂等性重复消费问题。
MQ幂等性问题根本在于Pro未正常接收ACK,可能网络抖动、网络中断。
实现
Con消费开始时,将ID放入Redis BitMap。Pro每次生产数据时,从BitMap对应位置若不能取出ID,则生产消息发送,否则不进行消息发送。
Q:万一Con、Pro的Redis命令执行失败咋办,虽出现重复消费 && 出现Redis非正常执行命令可能性极低,可万一?
A:可在Redis命令执行失败时,将消息落库,每日用定时器,处理这种极特殊消息。
3.2.3 使用分布式锁
3.2.4 设置数据库唯一索引
- 点赞
- 收藏
- 关注作者
评论(0)