文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

RocketMQ 事务消息初体验

2024-11-30 11:25

关注

1 应用场景

举一个电商场景的例子:用户购物车结算时,系统会创建支付订单。

用户支付成功后支付订单的状态会由未支付修改为支付成功,然后系统给用户增加积分。

通常我们会使用普通消费方案,该方案能够发挥 MQ 的优势:异步和解耦 ,  同时架构设计非常简单。

图片

  1. 用户购物车结算时,系统创建支付订单;
  2. 支付成功后,更新订单的状态从未支付修改为支付成功;
  3. 发送一条普通消息到消息队列服务端;
  4. 积分服务消费消息,添加积分记录。

但该方案有个非常直观的缺点:容易出现不一致的现象。

  1. 假如先发送消息,后修改订单状态,消息发送成功,订单没有执行成功,需要回滚整个事务(订单数据事务回滚,积分服务消费时,需要先反查事务状态,若事务提交,才能插入积分记录)。
  2. 假如先修改订单状态,后发送消息,订单状态修改成功,但消息发送失败,需要补偿操作才能保持最终一致。
  3. 假如先修改订单,后发送消息,订单状态修改成功,但消息发送超时,此时无法判断需要回滚订单还是提交订单变更。

我们看到,为了完善普通消费方案,业务层还需要做到两点:补偿机制和提供事务状态查询接口。

要做到这两点,难不难呢?

不难,但是业务层代码会比较混乱,更优的方案还是得从中间件层面解决。

2 功能原理

RocketMQ 事务消息是支持在分布式场景下保障消息生产和本地事务的最终一致性。交互流程如下图所示:

图片

1、生产者将消息发送至 Broker 。

2、Broker 将消息持久化成功之后,向生产者返回 Ack 确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。

3、生产者开始执行本地事务逻辑。

4、生产者根据本地事务执行结果向服务端提交二次确认结果( Commit 或是 Rollback ),Broker 收到确认结果后处理逻辑如下:

5、在断网或者是生产者应用重启的特殊情况下,若 Broker 未收到发送者提交的二次确认结果,或 Broker 收到的二次确认结果为 Unknown 未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。

  1. 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
  2. 生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。

笔者认为事务消息的精髓在于:

  1. 本地事务执行成功,消费者才能消费事务消息;
  2. 消息回查本身就是补偿机制的实现,事务生产者需提供了事务状态查询接口。

3 实战例子

为了便于大家理解事务消息 ,笔者新建一个工程用于模拟支付订单创建、支付成功、赠送积分的流程。

首先,我们创建一个真实的订单主题:order-topic 。

图片

然后在数据库中创建三张表 订单表、事务日志表、积分表。

图片

最后我们创建一个 Demo 工程,生产者模块用于创建支付订单、修改支付订单成功,消费者模块用于新增积分记录。

图片

接下来,我们展示事务消息的实现流程。

1、创建支付订单

调用订单生产者服务创建订单接口 ,在 t_order 表中插入一条支付订单记录。

图片

2、调用生产者服务修改订单状态接口

接口的逻辑就是执行事务生产者的 sendMessageInTransaction  方法。

图片

生产者端需要配置事务生产者和事务监听器。

图片

发送事务消息的方法内部包含三个步骤 :

图片

事务生产者首先发送半事务消息,发送成功后,生产者才开始执行本地事务逻辑。

事务监听器实现了两个功能:执行本地事务和供 Broker 回查事务状态 。

图片

执行本地事务的逻辑内部就是执行 orderService.updateOrder 方法。

方法执行成功则返回 LocalTransactionState.COMMIT_MESSAGE , 若执行失败则返回 LocalTransactionState.ROLLBACK_MESSAGE 。

图片

需要注意的是: orderService.updateOrder 方法添加了事务注解,并将修改订单状态和插入事务日志表放进一个事务内,避免订单状态和事务日志表的数据不一致。

最后,生产者根据本地事务执行结果向 Broker 提交二次确认结果。

Broker 收到生产者确认结果后处理逻辑如下:

3、积分消费者消费消息,添加积分记录

当 Broker 将半事务消息标记为可投递时,积分消费者就可以开始消费主题 order-topic 的消息了。

图片

积分消费者服务,我们定义了消费者组名,以及订阅主题和消费监听器。

图片

在消费监听器逻辑里,幂等非常重要 。当收到订单信息后,首先判断该订单是否有积分记录,若没有记录,才插入积分记录。

而且我们在创建积分表时,订单编号也是唯一键,数据库中也必然不会存在相同订单的多条积分记录。

4 总结

RocketMQ 事务消息是支持在分布式场景下保障消息生产和本地事务的最终一致性。

编写一个实战例子并不复杂,但使用事务消息时需要注意如下三点:

1、事务生产者和消费者共同协作才能保证业务数据的最终一致性;

2、事务生产者需要实现事务监听器,并且保存事务的执行结果(比如事务日志表) ;

3、消费者要保证幂等。消费失败时,通过重试、告警+人工介入等手段保证消费结果正确。

笔者会在后续的文章里,详细解析事务消息的实现原理,敬请期待。

实战代码地址:

https://github.com/makemyownlife/rocketmq4-learning

来源:勇哥java实战分享内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯