MQ实现事务消息

2 篇文章 0 订阅
2 篇文章 0 订阅

参考文章:吃透此文,MQ会被你玩的出神入化

文章很有实际项目的参考意义,摘取部分文章

如何确保本地事务执行成功的情况下,消息一定会投递成功;或者本地事务执行失败的情况下,消息取消投递,这也就是常说的事务消息。

2.1、电商中有这样的一个场景

  1. 下单成功之后送积分的操作,我们使用 mq 来实现

  2. 下单成功之后,投递一条消息到 mq,积分系统消费消息,给用户增加积分

我们主要讨论一下,下单及投递消息到 mq 的操作,如何实现?每种方式优缺点?

2.2、方式一:业务事务中投递消息

过程

  • step1:开启本地事务

  • step2:生成购物订单

  • step3:投递消息到 mq

  • step4:提交本地事务

这种方式是将发送消息放在了事务提交之前。

可能存在的问题

  • step3 发生异常:导致 step4 失败,商品下单失败,直接影响到商品下单业务

  • step4 发生异常,其他 step 成功:商品下单失败,消息投递成功,给用户增加了积分

2.3、方式二:先业务事务、后投递消息

下面我们换种方式,我们将发送消息放到事务之后进行。

过程

  • step1:开启本地事务

  • step2:生成购物订单

  • step3:提交本地事务

  • step4:投递消息到 mq

可能会出现的问题

step4 发生异常,其他 step 成功:导致商品下单成功,投递消息失败,用户未增加积分

上面两种是比较常见的做法,也是最容易出错的。

2.4、方式三:通过事务消息记录实现

  • step1:开启本地事务

  • step2:生成购物订单

  • step3:本地库中插入一条需要发送消息的记录 t_msg_record

  • step3:提交本地事务

  • step5:新增一个定时器,轮询 t_msg_record,将待发送的记录投递到 mq 中

这种方式借助了数据库的事务,业务和消息记录作为了一个原子操作,业务成功之后,消息日志必定是存在的。解决了前两种方式遇到的问题。如果我们的业务系统比较单一,可以采用这种方式。

对于微服务化的情况,上面这种方式不是太好,每个服务都需要上面的操作;也不利于扩展。

2.5、方式四:2 阶段投递消息

增加一个消息服务消息库,负责消息的落库、将消息发送投递到 mq。

  • step1:开启本地事务

  • step2:生成购物订单

  • step3:当前事务库插入一条日志:生成一个唯一的业务 id(bus_id),将 bus_id 和订单关联起来保存到当前事务所在的库中

  • step4:调用消息服务:携带 bus_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息 id(msg_id)

  • step5:提交本地事务

  • step6:如果上面都成功,调用消息服务,将消息投递到 mq 中;如果上面有失败的情况,则调用消息服务取消消息的发送

能想到上面这种方式,已经算是有很大进步了,我们继续分析一下可能存在的问题:

  1. 系统中增加了一个消息服务,商品下单操作依赖于该服务,业务对该服务依赖性比较高,当消息服务不可用时,整个业务将不可用。

  2. 若 step6 失败,消息将处于待发送状态,此时业务方需要提供一个回查接口(通过 bus_id 查询),验证业务是否执行成功;消息服务需新增一个定时任务,对于状态为待发送状态的消息做补偿处理,检查一下业务是否处理成功;从而确定消息是投递还是取消发送

  3. step4 依赖于消息服务,如果消息服务性能不佳,会导致当前业务的事务提交时间延长,容易产生死锁,并导致并发性能降低。我们通常是比较忌讳在事务中做远程调用处理的,远程调用的性能和时间往往不可控,会导致当前事务变为一个大事务,从而引发其他故障。

2.6、方式五:2 阶段投递消息优化

在以上方式中,我们继续改进,进而出现了更好的一种方式:

  • step1:生成一个全局唯一业务消息 id(bus_msg_id),调用消息服务,携带 bus_msg_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息 id(msg_id)

  • step2:开启本地事务

  • step3:生成购物订单

  • step4:当前事务库插入一条日志(将 step3 中的业务和 bus_msg_id 关联起来)

  • step5:提交本地事务

  • step6:分 2 种情况:如果上面都成功,调用消息服务,将消息投递到 mq 中;如果上面有失败的情况,则调用消息服务取消消息的发送

若 step6 失败,消息将处于待发送状态,此时业务方需要提供一个回查接口(通过 bus_msg_id 查询),验证业务是否执行成功;

消息服务需新增一个定时任务,对于状态为待发送状态的消息做补偿处理,检查一下业务是否处理成功;从而确定消息是投递还是取消发送。

方式五和方式四对比,比较好的一个地方:将调用消息服务,消息落地操作,放在了事务之外进行,这点小的改进其实算是一个非常好的优化,减少了本地事务的执行时间,从而可以提升并发量,阿里有个消息中间件RocketMQ就支持方式 5 这种,大家可以去用用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值