可以看到这样的处理方法会使得每一步的操作都比较原子,而原子则意味着是小事务,小事务则意味着使用消息表+事务的方案显得可行 。
然而,这太复杂了!这把一个本来连续的代码逻辑割裂成多个系统多次消息交互!那还不如业务代码层面上加锁实现呢 。
更通用的解决方案上面消息表+本地事务的方案之所以有其局限性和并发的短板,究其根本是因为它依赖于关系型数据库的事务,且必须要把事务包裹于整个消息消费的环节 。
如果我们能不依赖事务而实现消息的去重,那么方案就能推广到更复杂的场景例如:RPC、跨库等 。
例如,我们依旧使用消息表,但是不依赖事务,而是针对消息表增加消费状态,是否可以解决问题呢?
基于消息幂等表的非事务方案

文章插图
以上是去事务化后的消息幂等方案的流程,可以看到,此方案是无事务的,而是针对消息表本身做了状态的区分:消费中、消费完成 。只有消费完成的消息才会被幂等处理掉 。
而对于已有消费中的消息,后面重复的消息会触发延迟消费(在RocketMQ的场景下即发送到RETRY TOPIC),之所以触发延迟消费是为了控制并发场景下,第二条消息在第一条消息没完成的过程中,去控制消息不丢(如果直接幂等,那么会丢失消息(同一个消息id的话),因为上一条消息如果没有消费完成的时候,第二条消息你已经告诉broker成功了,那么第一条消息这时候失败broker也不会重新投递了)
上面的流程不再细说,后文有github源码的地址,读者可以参考源码的实现,这里我们回头看看我们一开始想解决的问题是否解决了:
- 消息已经消费成功了,第二条消息将被直接幂等处理掉(消费成功) 。
- 并发场景下的消息,依旧能满足不会出现消息重复,即穿透幂等挡板的问题 。
- 支持上游业务生产者重发的业务重复的消息幂等问题 。
关于第二个问题是如何解决的?主要是依靠插入消息表的这个动作做控制的,假设我们用MySQL作为消息表的存储媒介(设置消息的唯一ID为主键),那么插入的动作只有一条消息会成功,后面的消息插入会由于主键冲突而失败,走向延迟消费的分支,然后后面延迟消费的时候就会变成上面第一个场景的问题 。
关于第三个问题,只要我们设计去重的消息键让其支持业务的主键(例如订单号、请求流水号等),而不仅仅是messageId即可 。所以也不是问题 。
此方案是否有消息丢失的风险?如果细心的读者可能会发现这里实际上是有逻辑漏洞的,问题出在上面聊到的个三问题中的第2个问题(并发场景),在并发场景下我们依赖于消息状态是做并发控制使得第2条消息重复的消息会不断延迟消费(重试) 。但如果这时候第1条消息也由于一些异常原因(例如机器重启了、外部异常导致消费失败)没有成功消费成功呢?也就是说这时候延迟消费实际上每次下来看到的都是
推荐阅读
- 网友热议|新郎新娘同名同姓同年生 两家相距不足百米!网友:娃娃可以叫洋幂
- 杨幂婚礼的伴娘是谁?
- 十二岁女孩失踪九天最新消息 9岁失踪女孩最新进展
- |杨幂五月首封造型翻车,脖子怪异脸型回归年轻时,还不如私服好看
- 面试官竟然问我消息队列为啥会丢失消息?幸亏我总结了全套八股文
- |有一种裙子,叫杨幂的“虎皮裙”,看似普通,却不是谁都能穿好看
- 靳东|终于等到靳东新剧要播的消息了,积压了1年,阵容奢华,有剧追了
- 日照明日叶公司最新消息,明日叶健康料理
- 土豆丝|好消息!李晓霞找到新工作,杨倩瘦身成功,张常宁陪富二代郊游
- netty实现websocket客户端与服务端消息透传
