RocketMQ 事务消息

事务消息是 RocketMQ 的高级特性之一 。这篇文章,笔者会从应用场景功能原理实战例子三个模块慢慢为你揭开事务消息的神秘面纱。

1 应用场景

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

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

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

图片

  1. 用户购物车结算时,系统创建支付订单;

  2. 支付成功后,更新订单的状态从未支付修改为支付成功;

  3. 发送一条普通消息到消息队列服务端;

  4. 积分服务消费消息,添加积分记录。

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

  1. 假如先发送消息,后修改订单状态,消息发送成功,订单没有执行成功,需要回滚整个事务(订单数据事务回滚,积分服务消费时,需要先反查事务状态,若事务提交,才能插入积分记录)。

  2. 假如先修改订单状态,后发送消息,订单状态修改成功,但消息发送失败,需要补偿操作才能保持最终一致。

  3. 假如先修改订单,后发送消息,订单状态修改成功,但消息发送超时,此时无法判断需要回滚订单还是提交订单变更。

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

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

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

2 功能原理

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

图片

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

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

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

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

  • 二次确认结果为 Commit :Broker 将半事务消息标记为可投递,并投递给消费者。

  • 二次确认结果为 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 收到生产者确认结果后处理逻辑如下:

  • 二次确认结果为 Commit :Broker 将半事务消息标记为可投递,并投递给消费者。

  • 二次确认结果为 Rollback :Broker 将回滚事务,不会将半事务消息投递给消费者。

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

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

图片

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

图片

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

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

4 总结

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【雕爷学编程】MicroPython动手做(29)——物联网之SIoT

知识点:什么是掌控板? 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片,支持WiFi和蓝牙双模通信,可作为物联网节点,实现物联网应用。同时掌控板上集成了OLED…

阿里云AK创建

要在阿里云上创建 Access Key(AK),您需要按照以下步骤进行操作: 登录到阿里云控制台([https://www.aliyun.com/?utm_contentse_1014243503))。 点击右上方的主账号,点击“AccessKey管理”。 …

Jmeter如何添加插件

一、前言 ​ 在我们的工作中,我们可以利用一些插件来帮助我们更好的进行性能测试。今天我们来介绍下Jmeter怎么添加插件? 2023最新Jmeter接口测试从入门到精通(全套项目实战教程) 二、插件管理器 ​ 首先我们需要下载插件管理器j…

【前端知识】React 基础巩固(四十六)——自定义Hook的应用

React 基础巩固(四十六)——自定义Hook的应用 一、自定义Hook的应用 自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上而言,它并不算React的特性。 实现组件创建/销毁时打印日志 import React, { memo, useEffect, useState } from "react…

【图像分类】CNN+Transformer结合系列.2

介绍几篇利用CNNTransformer实现图像分类的论文:CMT(CVPR2022),MaxViT(ECCV2022),MaxViT(ECCV2022),MPViT(CVPR2022)。主要是说明Transformer的局限性&#x…

Vue+Nodejs+Express+Minio 实现本地图片上传

安装Minio,Minio server和Minio client都要下载可以自定义安装目录 安装完成之后,可以将minio配置成环境变量方便使用 配置了环境变量启动命令式 minio server start,默认账号密码minioadmin和minioadmin,点击9000端口的这个链接,即可访问客户端 nodejs连接Minio,简易服务进…

顺序表、链表刷题指南(力扣OJ)

目录 前言 题目一:删除有序数组中的重复项 思路: 题解: 题目二:合并两个有序数组 思路: 分析: 题解: 题目三:反转链表 思路: 分析: 题解: 题目四&…

springboot自定义错误消息

为了提供自定义错误消息提示,springboot在resources目录下,有一个文件ValidationMessages.properties 用于存储 验证错误的消息提示: 比如: 这样一个ValidationMessage.properties username.notempty用户名不能为空 username.len…

如何压缩高清PDF文件大小?将PDF文件压缩到最小的三个方法

PDF格式是一种非常常用的文档格式,但是有时候我们需要将PDF文件压缩为更小的大小以便于传输和存储。在本文中,我们将介绍三种PDF压缩的方法,包括在线PDF压缩、利用软件PDF压缩以及使用WPS缩小pdf。 首先,在线PDF压缩是最常用的方…

华云安参编的《云原生安全配置基线规范》正式发布

由中国信息通信研究院(以下简称“中国信通院”)、中国通信标准化协会主办的第十届可信云大会云原生安全分论坛于7月26日在北京国际会议中心成功召开。作为大会上展示的成果之一,由中国信通院联合行业领先企业共同编写的《云原生安全配置基线规…

寒假作业(蓝桥杯2016年省赛C++A组第6题 )

题目: 注:蓝桥杯2016年省赛CA组第6题 请填写表示方案数目的整数。 题解: 由题可知这是一道全排列问题,因此我们可以使用c的next_permutation函数对于1-13的数字进行全排列即可,并每次排列判断是否满足题意。 注意…

无边界野望:高通也有痛点?

外界给予高通的称呼无非是性价屠夫,因为即可“拳打”联发科,又能四度包下上海ChinaJoy展馆做游戏技术支持的王者,对高通来说它的路是无边界的,也是跨界之路上前行的“诗和远方”。 这里,既有生成式AI在终端侧布局、与…

React之组件的生命周期

React之组件的生命周期 一、概述二、整体说明三、挂载阶段四、更新阶段五、卸载阶段 一、概述 生命周期:一个事务从创建到最后消亡经历的整个过程组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程意义:理解组件的生…

使用IPSEC VPN 在有防火墙的场景和有NAT转换的场景下实现隧道通信实验

目录 一、在有防火墙的场景 1、为所有设备配置对应ip地址: 2、进入两个防火墙实现公网互通 3、测试公网是否互通 4、进入SW1配置IPSEC VPN 5、进入SW2配置IPSEC VPN 6、配置策略方向ESP的流量 7、尝试使用PC1访问PC2 二、在有NAT地址转换的场景 1、为新增加…

视频号将不支持添加企业微信:私域流量受阻

我是卢松松,点点上面的头像,欢迎关注我哦! 以前大家一直嚷嚷着说微信视频号企业微信私域流量神器,这下好了,从今日起这个功能没了,以后新的视频号不能绑定企业微信了,但老视频号还可以。 昨天…

招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计

功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&…

【数据结构】二叉树、二叉搜索树、平衡二叉树、红黑树、B树、B+树

概述 二叉树(Binary Tree):每个节点最多有两个子节点(左子节点和右子节点),没有限制节点的顺序。特点是简单直观,易于实现,但查找效率较低。 二叉搜索树(Binary Search…

烘焙小程序蛋糕店烘焙店源码点心店小程序源码

本系统开发使用JAVA技术栈开发 使用uniapp技术栈 支持微信小程序 ,对接打印机,对接第三方同城跑腿平台 用户端使用:uniapp 管理端使用:vueelementui 后台服务使用:springbootjpa

Java版本工程行业管理系统源码-专业的工程管理软件-提供一站式服务 em

​ 工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

不同语言操作符的优先级

看到标题,可能会心生疑惑: 这么基础且重要的操作,不同语言不应该是一致的吗? 并不一定,比如对于右移运算和加法运算,Go就与其他多数语言表现得不一致: Go: package mainimport "fmt"func main() …