背景
上游系统A,需要通过mq向下游系统B发送下单请求,B系统通过mq把订单号回传给A系统
mysql事务与mq消息
系统A在发送mq之前,有自己的mysql事务,如何处理mysql事务与mq消息?
方案 | 执行顺序 | 说明 |
---|---|---|
方案一 | 1. 发送mq 2. 开启事务 3. CURD 4. 提交事务 |
如果CURD失败,mq无法撤销 |
方案二 | 1. 开启事务 2. CURD 3. 发送mq 4. 提交事务 |
由于各种原因,提交事务可能失败,mq无法撤销 |
方案三 | 1. 开启事务 2. CURD 3. 提交事务 4. 发送mq |
提交事务后再发送mq,无法保证mq一定发送成功 |
方案四 | 用rocketmq的事务消息 | 强依赖于rocketmq(如果不考虑后面更换mq,这个方案也可以) |
方案五 | 1. 开启事务 2. CURD 3. 向本地消息表插一行记录 4. 提交事务 5. 发送mq |
通过本地消息表,保证mq消息一定发送成功,从而达到最终一致性 |
方案五的本地消息表,存储的是mq的topic、消息内容、状态等信息,除了需要新建表,还需要引入定时任务,具体流程如下
image.png
幂等
mq消息有可能重复推送,因此消费者一定要做幂等。
假设下单时,mq消息里只有商品id、商品数量,B系统重复消费到mq消息时,很难标识这两条消息是不是同一笔订单,因此最好是A系统产生一个唯一id,放到mq消息里。
B系统的幂等实现如下
说明
- 如果同一个mq消息并发重复投递,请求2到的时候,请求1还没执行完,查不到uuid对应的下单记录,会重复执行下单逻辑,因此需要加上分布式锁。
- 请求2需要阻塞到请求1执行完成,然后返回请求1生成的订单号,因为这样才做到幂等(重复请求返回结果一致)。
- 如果不返回uuid,A系统拿到订单号,不知道对应哪一笔订单。
B系统生成的订单号通过mq回传给A系统,也有可能重复投递,因此A系统也需要有幂等逻辑
image.png
如果分布式事务中,涉及到一些非常重要、敏感的接口,比如转钱,即使下游说已经做了幂等,上游最好不要100%信任,万一下游的幂等有bug呢。
以本文的case为例,最好要求下游提供一个“根据uuid查询订单号”的接口,在重试的代码里,先调查询接口,查不到订单号再重试。
网友评论