Springboot+RocketMQ通过事务消息优雅的实现订单支付功能

目录

1. 事务消息

1.1 RocketMQ事务消息的原理

1.2 RocketMQ订单支付功能设计


1. 事务消息

RocketMQ的事务消息,是指发送消息事件和其他事件需要同时成功或同时失败。比如银行转账, A银行的某账户要转一万元到B银行的某账户。A银行发送“B银行账户增加一万元”这个消息,要和“从A银 行账户扣除一万元”这个操作同时成功或者同时失败。RocketMQ采用两阶段提交的方式实现事务消息。

1.1 RocketMQ事务消息的原理

  1. 半事务消息发送:生产者将半事务消息发送至RocketMQ服务端。

  2. 消息持久化及返回Ack确认:RocketMQ服务端接收到半事务消息并持久化成功后,向生产者返回Ack确认消息已经发送成功。此时消息状态为半事务消息。

  3. 执行本地事务逻辑:根据发送结果执行本地事务,如果写入失败,此时half消息对业务不可见,本地事务逻辑不执行。

  4. 提交二次确认结果:根据本地事务状态执行Commit或者Rollback。RocketMQ 的事务消息分为3种状态,分别是提交状态、回滚状态、未知状态。
    TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息。
    TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
    TransactionStatus.Unknown: 未知状态,它代表需要检查消息队列来确定状态。

  5. 消息回查:(1) 对没有Commit/Rollback的事务消息,从服务端发起一次回查 (2) Producer收到回查消息,检查回查消息对应的本地事务的状态 (3) 根据本地事务状态,重新Commit或者Rollback。第一次回查后仍未获取到事务状态,则之后每隔30s会再次回查,最多重试15次,超过了就会默认丢弃此消息。

1.2 RocketMQ订单支付功能设计

数据库设计

/*
SQLyog Community v13.2.0 (64 bit)
MySQL - 8.0.33 : Database - shop
*********************************************************************
*//*!40101 SET NAMES utf8 */;/*!40101 SET SQL_MODE=''*/;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`shop` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;USE `shop`;/*Table structure for table `shop_order` */DROP TABLE IF EXISTS `shop_order`;CREATE TABLE `shop_order` (`id` VARCHAR(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT '订单id',`total_num` INT DEFAULT NULL COMMENT '数量合计',`moneys` INT DEFAULT NULL COMMENT '金额合计',`pay_type` VARCHAR(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '支付类型,1、在线支付、0 货到付款',`create_time` DATETIME DEFAULT NULL COMMENT '订单创建时间',`update_time` DATETIME DEFAULT NULL COMMENT '订单更新时间',`pay_time` DATETIME DEFAULT NULL COMMENT '付款时间',`consign_time` DATETIME DEFAULT NULL COMMENT '发货时间',`end_time` DATETIME DEFAULT NULL COMMENT '交易完成时间',`username` VARCHAR(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '用户名称',`recipients` VARCHAR(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '收货人',`recipients_mobile` VARCHAR(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '收货人手机',`recipients_address` VARCHAR(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '收货人地址',`weixin_transaction_id` VARCHAR(30) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '交易流水号',`order_status` INT DEFAULT NULL COMMENT '订单状态,0:未完成,1:已完成,2:已退货',`pay_status` INT DEFAULT NULL COMMENT '支付状态,0:未支付,1:已支付,2:支付失败',`is_delete` INT DEFAULT NULL COMMENT '是否删除',PRIMARY KEY (`id`),KEY `create_time` (`create_time`),KEY `status` (`order_status`),KEY `payment_type` (`pay_type`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin;/*Data for the table `shop_order` *//*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

添加RocketMQ依赖

<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>

bootstrap.yaml配置

server:port: 8085
spring:application:name: mall-orderdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456cloud:nacos:config:file-extension: yamlserver-addr: localhost:8848discovery:#Nacos的注册地址server-addr: localhost:8848rocketmq:name-server: localhost:9876producer:group: test-group-producer

Service层

public interface OrderService extends IService<Order> {//添加订单void add(Order order);//修改订单支付状态void pay(String id);
}@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>implements OrderService{@AutowiredOrderMapper orderMapper;@Transactional(rollbackFor = Exception.class)@Overridepublic void add(Order order) {order.setCreateTime(new Date());orderMapper.insert(order); //这里仅仅生成订单,还有扣减库存等等一系列操作省略}@Transactional(rollbackFor = Exception.class)@Overridepublic void pay(String id) {//模拟支付完成,修改订单的支付状态Order order = orderMapper.selectById(id);order.setPayStatus(1);order.setPayTime(new Date());orderMapper.updateById(order);}
}

创建生产者

@RestController
@Slf4j
public class TestController {@AutowiredOrderMapper orderMapper;@AutowiredRocketMQTemplate rocketMQTemplate;@RequestMapping("/send")public String send(){String id = UUID.randomUUID().toString();String msg = "订单"+id+"支付成功";Order order=new Order();order.setId(id);order.setCreateTime(new Date());order.setMoneys(100);order.setUsername("张三");Message<String> message = MessageBuilder.withPayload(msg).setHeader("key",id).build();TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction("order", message, order);String transactionId = result.getTransactionId();String status = result.getSendStatus().name();log.info("发送消息成功 transactionId={} status={} ",transactionId,status);return "success";}
}

创建消费者

@Component
@Slf4j
@RocketMQMessageListener(consumerGroup = "test-consumer",topic = "order",messageModel = MessageModel.CLUSTERING)
public class RocketMQListen implements RocketMQListener<MessageExt> {@Overridepublic void onMessage(MessageExt messageExt) {String body = new String(messageExt.getBody(), StandardCharsets.UTF_8);System.out.println(body);}
}

生产者消息监听器

@Component
@RocketMQTransactionListener
public class TransactionMsgListener implements RocketMQLocalTransactionListener {@AutowiredOrderService orderService;@Overridepublic RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {Order order = (Order) o;try {//生成订单orderService.add(order);return RocketMQLocalTransactionState.UNKNOWN;}catch (Throwable throwable){throwable.printStackTrace();return RocketMQLocalTransactionState.ROLLBACK;}}@Overridepublic RocketMQLocalTransactionState checkLocalTransaction(Message message) {String key = message.getHeaders().get("key").toString();System.out.println("回查订单id "+key+" 回查时间"+new Date());Order order = orderService.getById(key);if(order!=null) {long l = new Date().getTime() - order.getCreateTime().getTime();long time = l / (1000 * 60);//超时1分钟后,就会把未支付的订单进行删除if (time > 1) {orderService.removeById(key);System.out.println("订单" + key + "删除");//订单,库存等一系列操作return RocketMQLocalTransactionState.ROLLBACK;}Integer payStatus = order.getPayStatus();if (payStatus == 1) {return RocketMQLocalTransactionState.COMMIT;}return RocketMQLocalTransactionState.UNKNOWN;}elsereturn RocketMQLocalTransactionState.ROLLBACK;}
}

测试

这里通过生产者发送五个事务消息,生成五个订单,然后两个订单在一分钟内修改支付状态为已支付,超时一分钟未支付就会删除订单回退。运行截图如下:

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

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

相关文章

高级分布式系统-第3讲 网络与网络互联

万维网的诞生 1957年10月4日&#xff0c; 苏联发射了人类第一颗人造卫星—斯普特尼克一号 美国政府震惊不已。 他们认为&#xff0c; 在日趋激烈的冷战对抗中&#xff0c; 自己已经全面落后于苏联。 为了扭转这一局面&#xff0c; 美国国防部很快于1958 年 2 月组建了一个神秘…

Fastdata极数公司介绍

【写在开头】 前不久看到一句话和一个新闻&#xff0c;“数据作为主要生产要素&#xff0c;以资源化为起点&#xff0c;经历资产化、资本化阶段&#xff0c;逐步实现数据价值。” 2023年10月25日&#xff0c;国家数据局正式揭牌&#xff0c;由国家发展和改革委员会管理。 初看…

linux安装node.js

先去官网下载对应的版本 官网&#xff1a;https://nodejs.org/en/download 选择对应的版本&#xff0c;点这个&#xff0c;直接去虚拟机上面安装 # apt的系统 apt install -y wget # yum的系统 yum install -y wget下载包 wget https://nodejs.org/dist/v20.10.0/node-v20.…

【小白专用】(C#)用户、角色、权限控制体系

我们在开发很多项目的时候,都会用到用户权限管理,我也在很多项目里做过权限控制,所以,我也总结出一套条理清晰的角色权限控制体系。本文采用RBAC&#xff08;Role Based Access Control&#xff09;的基本思想&#xff0c;RBAC&#xff08;角色访问控制&#xff09;的基本思想可…

Java可视化大屏智慧工地云平台源码(SaaS模式)

智慧工地是一种崭新的工程现场一体化管理模式&#xff0c;是互联网与传统建筑行业的深度融合。它充分利用移动互联、物联网、云计算、大数据等新一代信息技术&#xff0c;围绕人、机、料、法、环等各方面关键因素&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方…

5G前装搭载率即将迈过10%大关,车载通讯进入多层次增长通道

对于智能化来说&#xff0c;车载通讯性能的提升&#xff0c;对于相关功能的用户体验优化、进一步减少通讯时延以及打开应用新空间&#xff0c;至关重要。 目前&#xff0c;2G/3G正在进入运营商逐步关闭运营的阶段&#xff0c;4G依然是主力&#xff0c;但5G也在迎来新的增长机会…

imazing破解版百度云2.17.3(附激活许可证下载)

iMazing是一款强大的 iOS 设备管理软件&#xff0c;不管是 iPhone、iPad 或 iPod Touch 设备&#xff0c;只要将 iOS 设备连接到计算机&#xff0c;就可以处理不同类型的数据。 iPhone 和 iPad 备份 借助 iMazing 的独有 iOS 备份技术&#xff08;无线、隐私和自动&#xff09…

强化学习的数学原理学习笔记 - 策略梯度(Policy Gradient)

文章目录 概览&#xff1a;RL方法分类策略梯度&#xff08;Policy Gradient&#xff09;Basic Policy Gradient目标函数1&#xff1a;平均状态值目标函数2&#xff1a;平均单步奖励&#x1f7e1;PG梯度计算 &#x1f7e6;REINFORCE 本系列文章介绍强化学习基础知识与经典算法原…

速卖通跨境智星:解决IP及环境问题,实现批量注册轻松搞定

如果想要注册大批量的速卖通买家号&#xff0c;关键问题之一就是IP及浏览环境的管理。为了确保每个账号都能独立运行&#xff0c;使用独立的IP是必不可少的。近期&#xff0c;速卖通跨境智星备受关注&#xff0c;支持绑定代理IP&#xff0c;并内置反指纹技术&#xff0c;为用户…

WinForms中的UI卡死

WinForms中的UI卡死 WinForms中的UI卡死通常是由于长时间运行的操作阻塞了UI线程所导致的。在UI线程上执行的操作&#xff0c;例如数据访问、计算、文件读写等&#xff0c;如果耗时较长&#xff0c;会使得UI界面失去响应&#xff0c;甚至出现卡死的情况。 解决方法 为了避免…

USACO(美国计算机竞赛)详细介绍 如何备赛 训练资料整理

竞赛简介&#xff1a; USACO&#xff08;美国信息学奥林匹克竞赛&#xff09;初次举办于1992年&#xff0c;其官网是美国一个著名在线题库&#xff0c;更是美国中学生的官方竞赛网站&#xff0c;开设目的是为每年夏季举办的国际信息学奥林匹克竞赛&#xff08;IOI&#xff09;…

深度学习——R-CNN目标检测原理

R-CNN系列算法需要先产生候选区域&#xff0c;再对候选区域做分类和位置坐标的预测&#xff0c;这类算法被称为两阶段目标检测算法。近几年&#xff0c;很多研究人员相继提出一系列单阶段的检测算法&#xff0c;只需要一个网络即可同时产生候选区域并预测出物体的类别和位置坐标…

开源C语言库Melon:I/O线程模型

本文展示开源C语言库Melon中的I/O线程模型。在Melon中存在三种线程模型&#xff0c;本篇仅对I/O线程模型进行说明。 关于 Melon 库&#xff0c;这是一个开源的 C 语言库&#xff0c;它具有&#xff1a;开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。 Github …

如何创建容器搭建节点

1.注册Discord账号 https://discord.com/这是登录网址&#xff1a; https://discord.com/ 2.点击startnow注册&#xff0c;用discord注册或者邮箱注册都可&#xff0c;然后登录tickhosting Tick Hosting这是登录网址&#xff1a;Tick Hosting 3.创建servers 4.点击你创建的s…

欧洲编程语言四巨头

从左往右&#xff0c;依次是 尼克劳斯沃斯 (Niklaus Wirth)&#xff0c;迪杰斯特拉&#xff08;Edsger Dijkstra&#xff09;&#xff0c;霍尔&#xff08;Tony Hoare&#xff09; 尼克劳斯沃斯 (Niklaus Wirth) 瑞士人&#xff0c;一生发明了8种编程语言&#xff0c;其中最著…

暖冬志愿服务在行动

1月7日&#xff0c;为了传递温暖与关爱&#xff0c;由雅安市群团中心指导&#xff0c;雅安市志愿服务联合会支持&#xff0c;雅安市雨城区梦飞翔公益社会服务中心执行的“暖冬志愿服务行动”在雨城区上里镇、碧峰峡镇、八步镇等乡镇&#xff0c;走访慰问困境儿童&#xff0c;为…

淘宝搜索指定店铺所有商品接口API调用介绍-含请求key代码示例

接口名称&#xff1a;item_search_shop 请求地址&#xff1a;taobao.item_search_shop 接口说明&#xff1a;此接口用于获取指定淘宝/天猫店铺的所有商品&#xff0c;通过传入shop_id或者seller_id可以获取到该店铺的所有商品&#xff0c;支持翻页展示。每页返回60个商品&…

1.4~1.5链表复习,代码操作(反转链表(用栈解决,双指针),删除链表指定元素),链表选择题,广义表

删除链表内指定范围的数 思路是双指针&#xff0c;定义两个指针&#xff0c;一个去找当前这个数满不满足要求&#xff0c;然后另一个定义为删除区间的起点 &#xff0c; 当不满足时&#xff0c;两个指针同时向后移动&#xff1b;当满足时&#xff0c;前驱指针就不动了&#xf…

sqlmap性能优化

sqlmap性能优化 &#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f32d;&#x1f32d;&#…

SpringBoot多环境配置以及热部署

多环境配置 使用多环境配置的原因&#xff1a; 在SpringBoot项目的生命周期中&#xff0c;存在不同的环境&#xff0c;例如开发时的环境&#xff0c;测试时的环境&#xff0c;交付使用后的生产环境&#xff0c;每种环境的配置可能不一样&#xff0c;这种情况下可以通过多环境…