标签:事务所 生成 如何 record 异常 strong 用处 落地 入库
电商中有这样的一个场景:
我们主要讨论一下,下单及投递消息到mq的操作,如何实现?每种方式优缺点?
step1:start transaction
step2:生成订单
step3:投递消息到mq
step4:commit transaction
这种方式是将发送消息放在了事务提交之前,可能存在的问题:
step3发生异常
导致step4失败,下单失败,直接影响到下单业务
step4发生异常,其他step成功
下单失败,消息投递成功,给用户增加了积分
我们将发送消息放到事务之后进行:
step1:start transaction
step2:生成订单
step3:commit transaction
step4:投递消息到mq
可能会出现的问题:
step4发生异常,其他step成功
导致下单成功,投递消息失败,用户未增加积分
上面两种是比较常见的做法,也是最容易出错的。
step1:start transaction
step2:生成订单
step3:本地库中插入一条需要发送消息的记录t_msg_record
step3:commit transaction
step5:新增一个定时器,轮询t_msg_record,将待发送的记录投递到mq中
这种方式借助了数据库的事务,业务和消息记录作为了一个原子操作,业务成功之后,消息日志必定是存在的。解决了前两种方式遇到的问题。如果我们的业务系统比较单一,可以采用这种方式。
对于微服务化的情况,上面这种方式不是太好,每个服务都需要上面的操作;也不利于扩展。
增加一个消息服务及消息库,负责消息的落库、将消息发送投递到mq。
step1:start transaction
step2:生成订单
step3:当前事务库插入一条日志:生成一个唯一的业务id(bus_id),将bus_id和订单关联起来保存到当前事务所在的库中
step4:调用消息服务:携带bus_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息id(msg_id)
step5:commit transaction
step6:如果上面都成功,调用消息服务,将消息投递到mq中;如果上面有失败的情况,则调用消息服务取消消息的发送
能想到上面这种方式,已经算是有很大进步了,我们继续分析一下可能存在的问题:
在以上方式中,我们继续改进,进而出现了更好的一种方式:
step1:生成一个全局唯一业务消息id(bus_msg_id),调用消息服务,携带bus_msg_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息id(msg_id)
step2:start transaction
step3:生成订单
step4:当前事务库插入一条日志(将step3中的业务和bus_msg_id关联起来)
step5:commit transaction
step6:分2中情况:如果上面都成功,调用消息服务,将消息投递到mq中;如果上面有失败的情况,则调用消息服务取消消息的发送
方式五和方式四对比,比较好的一个地方:将调用消息服务,消息落地操作,放在了事务之外进行,这点小的改进其实算是一个非常好的优化。
路人甲Java,只生产干货,公众号:javacode2018,喜欢的关注一下。
标签:事务所 生成 如何 record 异常 strong 用处 落地 入库
原文地址:https://www.cnblogs.com/itsoku123/p/10813423.html