一、缘起

MQ消息必达,架构上有两个核心设计点:

(1)消息落地

(2)消息超时、重传、确认

再次回顾消息总线核心架构,它由

发送端、服务端、固化存储、接收端

四大部分组成。

为保证消息的可达性,超时、重传、确认机制可能导致消息总线、或者业务方收到重复的消息,从而对业务产生影响。

举个栗子:

购买会员卡,上游支付系统负责给用户扣款,下游系统负责给用户发卡,通过MQ异步通知。不管是上半场的ACK丢失,导致MQ收到重复的消息,还是下半场ACK丢失,导致购卡系统收到重复的购卡通知,都可能出现,上游扣了一次钱,下游发了多张卡。

消息总线的幂等性设计至关重要,是本文将要讨论的重点。

二、上半场的幂等性设计

MQ消息发送上半场,即上图中的1-3

1,发送端MQ-client将消息发给服务端MQ-server

2,服务端MQ-server将消息落地

3,服务端MQ-server回ACK给发送端MQ-client

如果3丢失,发送端MQ-client超时后会重发消息,可能导致服务端MQ-server收到重复消息。

此时重发是MQ-client发起的,消息的处理是MQ-server,为了避免步骤2落地重复的消息,对每条消息,MQ系统内部必须生成一个inner-msg-id,作为去重和幂等的依据,这个内部消息ID的特性是:

(1)全局唯一

(2)MQ生成,具备业务无关性,对消息发送方和消息接收方屏蔽

有了这个inner-msg-id,就能保证上半场重发,也只有1条消息落到MQ-server的DB中,实现上半场幂等。

三、下半场的幂等性设计

 

MQ消息发送下半场,即上图中的4-6

4,服务端MQ-server将消息发给接收端MQ-client

5,接收端MQ-client回ACK给服务端

6,服务端MQ-server将落地消息删除

需要强调的是,接收端MQ-client回ACK给服务端MQ-server,是消息消费业务方的主动调用行为,不能由MQ-client自动发起,因为MQ系统不知道消费方什么时候真正消费成功。

如果5丢失,服务端MQ-server超时后会重发消息,可能导致MQ-client收到重复的消息。

此时重发是MQ-server发起的,消息的处理是消息消费业务方,消息重发势必导致业务方重复消费(上例中的一次付款,重复发卡),为了保证业务幂等性,业务消息体中,必须有一个biz-id,作为去重和幂等的依据,这个业务ID的特性是:

(1)对于同一个业务场景,全局唯一

(2)由业务消息发送方生成,业务相关,对MQ透明

(3)由业务消息消费方负责判重,以保证幂等

最常见的业务ID有:支付ID,订单ID,帖子ID等。

具体到支付购卡场景,发送方必须将支付ID放到消息体中,消费方必须对同一个支付ID进行判重,保证购卡的幂等。

有了这个业务ID,才能够保证下半场消息消费业务方即使收到重复消息,也只有1条消息被消费,保证了幂等。

三、总结

MQ为了保证消息必达,消息上下半场均可能发送重复消息,如何保证消息的幂等性呢?

上半场

MQ-client生成inner-msg-id,保证上半场幂等。

这个ID全局唯一,业务无关,由MQ保证。

下半场

业务发送方带入biz-id,业务接收方去重保证幂等。

这个ID对单业务唯一,业务相关,对MQ透明。

结论:幂等性,不仅对MQ有要求,对业务上下游也有要求。

原文链接:https://www.jianshu.com/p/8b77d4583bab

MQ之如何做到消息幂等 (转 优秀)的更多相关文章

  1. RabbitMQ 消息顺序、消息幂等、消息重复、消息事务、集群

    1. 消息顺序 场景:比如下单操作,下单成功之后,会发布创建订单和扣减库存消息,但扣减库存消息执行会先于创建订单消息,也就说前者执行成功之后,才能执行后者. 不保证完全按照顺序消费,在 MQ 层面支持 ...

  2. RocketMQ之消息幂等

    幂等(idempotent.idempotence)是一个数学与计算机学概念,常见于抽象代数中. 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同. 首先我们了解一下什么是 ...

  3. RocketMQ重试机制和消息幂等

    一.重试机制 由于MQ经常处于复杂的分布式系统中,考虑网络波动,服务宕机,程序异常因素,很有可能出现消息发送或者消费失败的问题.因此,消息的重试就是所有MQ中间件必须考虑到的一个关键点.如果没有消息重 ...

  4. Rabbit mq订阅方式获取消息并可设置持久化

    Rabbit 通过方式获取消息:订阅方式事实上是向queue注冊consumer,通过rpc向queue server发送注冊consumer的消息.rabbitMQ Server在收到消息后,依据消 ...

  5. 【mq读书笔记】消息消费队列和索引文件的更新

    ConsumeQueue,IndexFile需要及时更新,否则无法及时被消费,根据消息属性查找消息也会出现较大延迟. mq通过开启一个线程ReputMessageService来准时转发commitL ...

  6. 【mq读书笔记】消息拉取

    疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...

  7. 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)

    在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...

  8. IBM MQ扩大队列最大消息长度

    要设置MQ的最大消息长度,需要考虑同时设置队列管理,队列以及通道的最大消息长度. 具体操作如下: runmqsc 队列管理器名称 alter qmgr maxmsgl(10000000) 1 : al ...

  9. Kafka,Mq,Redis作为消息队列使用时的差异?

    redis 消息推送(基于分布式 pub/sub)多用于实时性较高的消息推送,并不保证可靠.其他的mq和kafka保证可靠但有一些延迟(非实时系统没有保证延迟).redis-pub/sub断电就清空, ...

随机推荐

  1. jzoj5913

    這道題我們可以套路的設置f[i]為當前節點為根的滿足條件方案數,然後枚舉根,計算必須包含當前根的方案 但是似乎很難計算 所以我們可以搞一個前綴和,將聯通塊的最大數和最小數相減<=k的方案和< ...

  2. Python面向对象(多态)

    day24 面向对象三大特性:多态 Python原生就是多态的.

  3. C语言的组成 以及预编译

    这么多年过去了,回头再来学习一下C语言,发现很多不一样的感觉 #include <stdio.h> int main(int argc, const char * argv[]) { pr ...

  4. OSS和CDN配置使用

    Oss和cdn目的就是: 1 把资源文件和程序分开存储 2 可以通过cdn缓存加速   下面介绍阿里云OSS和CDN如何配制 一  阿里云开通子RAM账户 1  不使用主账号访问OSS,需要创建子RA ...

  5. PHP基础记录

    1. require和require_once的区别 require_once()包涵是绝对路径 include() 和require() :语句包括并运行指定文件. include() 产生一个警告 ...

  6. phpstorm扩展

    1, CodeGlance 代码地图插件 &lt;img src="https://pic1.zhimg.com/50/v2-721f173bafcb9b60853819c32780 ...

  7. 解析ASP.NET WebForm和Mvc开发的区别 分类: ASP.NET 2013-12-29 01:59 11738人阅读 评论(5) 收藏

    因为以前主要是做WebFrom开发,对MVC开发并没有太深入的了解.自从来到创新工场的新团队后,用的技术都是自己以前没有接触过的,比如:MVC 和EF还有就是WCF,压力一直很大.在很多问题都是不清楚 ...

  8. springclould nginx转发 websocket400报错问题

    之前一直找原因一直围绕着nginx转发的问题 说头信息没设置全 然后nginx配置文件上加了这些 #http块加以下几行: map $http_upgrade $connection_upgrade ...

  9. android学习-ndk-build(androidstudio编译cocos2d-x库的cpp为so文件的解释)

    本文不作为ndk初学使用,只是对cpp等c++文件编译成so文件的过程中,参数含义,及ndk配置的解释.使用的技术比较旧. androidStudio使用gradle调用ndk-build工具编译c+ ...

  10. @RestController和@Controller注解的区别

    @RestController是@ResponseBody和@Controller注解的结合,当你return一个页面时,使用@Controller注解返回的是相应页面,使用@RestControll ...