RocketMQ实现分布式事务

有关RocketMQ实现分布式事务前面写了一篇博客

1、RocketMQ实现分布式事务原理

下面就这个项目做个整体简单介绍,并在文字最下方附上项目Github地址。

一、项目概述

1、技术架构

项目总体技术选型

SpringCloud(Finchley.RELEASE) + SpringBoot2.0.4 + Maven3.5.4 + RocketMQ4.3 +MySQL + lombok(插件)

有关SpringCloud主要用到以下四个组建

Eureka Server +config-server(配置中心)+ Eureka Client + Feign(服务间调用)

配置中心是用MySQL存储数据。

2、项目整体结构

config-service  # 配置中心
eureka # 注册中心
service-order #订单微服务
service-produce #商品微服务

各服务的启动顺序就安装上面的顺序启动。

大致流程

启动后,配置中心、订单微服务、商品微服务都会将信息注册到注册中心。

如果访问:localhost:7001(注册中心地址),以上服务都出现说明启动成功。

3、分布式服务流程

用户在订单微服务下单后,会去回调商品微服务去减库存。这个过程需要事务的一致性。

4、测试流程

页面输入:

http://localhost:9001/api/v1/order/save?userId=1&productId=1&total=4

订单微服务执行情况(订单服务事务执行成功)

商品微服务执行情况(商品服务事务执行成功)

当然你也可以通过修改参数来模拟分布式事务出现的各种情况。

二、MQ中生产者核心代码

这里展示下,生产者发送消息核心代码。

@Slf4j
@Component
public class TransactionProducer { /**
* 需要自定义事务监听器 用于 事务的二次确认 和 事务回查
*/
private TransactionListener transactionListener ;
/**
* 这里的生产者和之前的不一样
*/
private TransactionMQProducer producer = null;
/**
* 官方建议自定义线程 给线程取自定义名称 发现问题更好排查
*/
private ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
}); public TransactionProducer(@Autowired Jms jms, @Autowired ProduceOrderService produceOrderService) {
transactionListener = new TransactionListenerImpl(produceOrderService);
// 初始化 事务生产者
producer = new TransactionMQProducer(jms.getOrderTopic());
// 添加服务器地址
producer.setNamesrvAddr(jms.getNameServer());
// 添加事务监听器
producer.setTransactionListener(transactionListener);
// 添加自定义线程池
producer.setExecutorService(executorService); start();
} public TransactionMQProducer getProducer() {
return this.producer;
} /**
* 对象在使用之前必须要调用一次,只能初始化一次
*/
public void start() {
try {
this.producer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
} /**
* 一般在应用上下文,使用上下文监听器,进行关闭
*/
public void shutdown() {
this.producer.shutdown();
}
} /**
* @author xub
* @Description: 自定义事务监听器
* @date 2019/7/15 下午12:20
*/
@Slf4j
class TransactionListenerImpl implements TransactionListener { @Autowired
private ProduceOrderService produceOrderService ; public TransactionListenerImpl( ProduceOrderService produceOrderService) {
this.produceOrderService = produceOrderService;
} @Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
log.info("=========本地事务开始执行=============");
String message = new String(msg.getBody());
JSONObject jsonObject = JSONObject.parseObject(message);
Integer productId = jsonObject.getInteger("productId");
Integer total = jsonObject.getInteger("total");
int userId = Integer.parseInt(arg.toString());
//模拟执行本地事务begin=======
/**
* 本地事务执行会有三种可能
* 1、commit 成功
* 2、Rollback 失败
* 3、网络等原因服务宕机收不到返回结果
*/
log.info("本地事务执行参数,用户id={},商品ID={},销售库存={}",userId,productId,total);
int result = produceOrderService.save(userId, productId, total);
//模拟执行本地事务end========
//TODO 实际开发下面不需要我们手动返回,而是根据本地事务执行结果自动返回
//1、二次确认消息,然后消费者可以消费
if (result == 0) {
return LocalTransactionState.COMMIT_MESSAGE;
}
//2、回滚消息,Broker端会删除半消息
if (result == 1) {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
//3、Broker端会进行回查消息
if (result == 2) {
return LocalTransactionState.UNKNOW;
}
return LocalTransactionState.COMMIT_MESSAGE;
} /**
* 只有上面接口返回 LocalTransactionState.UNKNOW 才会调用查接口被调用
*
* @param msg 消息
* @return
*/
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
log.info("==========回查接口=========");
String key = msg.getKeys();
//TODO 1、必须根据key先去检查本地事务消息是否完成。
/**
* 因为有种情况就是:上面本地事务执行成功了,但是return LocalTransactionState.COMMIT_MESSAG的时候
* 服务挂了,那么最终 Brock还未收到消息的二次确定,还是个半消息 ,所以当重新启动的时候还是回调这个回调接口。
* 如果不先查询上面本地事务的执行情况 直接在执行本地事务,那么就相当于成功执行了两次本地事务了。
*/
// TODO 2、这里返回要么commit 要么rollback。没有必要在返回 UNKNOW
return LocalTransactionState.COMMIT_MESSAGE;
}
}

三、MQ消费端核心代码

这里展示下,消费端消费消息核心代码。消费端和普通消费一样。

@Slf4j
@Component
public class OrderConsumer {
private DefaultMQPushConsumer consumer;
private String consumerGroup = "produce_consumer_group"; public OrderConsumer(@Autowired Jms jms,@Autowired ProduceService produceService) throws MQClientException {
//设置消费组
consumer = new DefaultMQPushConsumer(consumerGroup);
// 添加服务器地址
consumer.setNamesrvAddr(jms.getNameServer());
// 添加订阅号
consumer.subscribe(jms.getOrderTopic(), "*");
// 监听消息
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
MessageExt msg = msgs.get(0);
String message = new String(msgs.get(0).getBody());
JSONObject jsonObject = JSONObject.parseObject(message);
Integer productId = jsonObject.getInteger("productId");
Integer total = jsonObject.getInteger("total");
String key = msg.getKeys();
log.info("消费端消费消息,商品ID={},销售数量={}",productId,total);
try {
produceService.updateStore(productId, total, key);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
log.info("消费失败,进行重试,重试到一定次数 那么将该条记录记录到数据库中,进行如果处理");
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
});
consumer.start();
System.out.println("consumer start ...");
}
}

至于完整的项目地址见GitHub。

如果对您能有帮助,就给个星星吧,哈哈!

GitHubd地址 https://github.com/yudiandemingzi/spring-cloud-rocketmq-transaction.git

晚安!

只要自己变优秀了,其他的事情才会跟着好起来(上将8)

分布式事务(4)---RocketMQ实现分布式事务项目的更多相关文章

  1. 分布式事务(3)---RocketMQ实现分布式事务原理

    分布式事务(3)-RocketMQ实现分布式事务原理 之前讲过有关分布式事务2PC.3PC.TCC的理论知识,博客地址: 1.分布式事务(1)---2PC和3PC原理 2.分布式事务(2)---TCC ...

  2. 【RocketMQ】【分布式事务】使用RocketMQ实现分布式事务

    参考地址:https://blog.csdn.net/zyw23zyw23/article/details/79070044 视频地址:https://v.youku.com/v_show/id_XO ...

  3. SpringCloud+RocketMQ实现分布式事务

    随着互联网公司的微服务越来越多,分布式事务已经成为了我们的经常使用的.所以我们来一步一步的实现基于RocketMQ的分布式事务.接下来,我们将要做的主题写出来. RocketMQ的分布式事务结构和说明 ...

  4. RocketMQ实现分布式事务

    相关文章:http://www.uml.org.cn/zjjs/201810091.asp(深入理解分布式事务,高并发下分布式事务的解决方案) 三种分布式事务: 1.基于XA协议的两阶段提交 2.消息 ...

  5. Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式)介绍

    jedis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务.管道及有jedis自身实现的分布式. 在这里对jedis关于事务.管道和分布式 ...

  6. 分布式事务(二)Java事务API(JTA)规范

    一.引子 既然出现了分布式场景(DTP模型), 大java也及时制定出一套规范来给各大应用服务器.数据库/mq等厂商使用,以方便管理互通--->JTA闪亮登场.JTA(Java Transact ...

  7. Zookeeper系列二:分布式架构详解、分布式技术详解、分布式事务

    一.分布式架构详解 1.分布式发展历程 1.1 单点集中式 特点:App.DB.FileServer都部署在一台机器上.并且访问请求量较少 1.2  应用服务和数据服务拆分  特点:App.DB.Fi ...

  8. 9、redis之事务2-Jedis的八种调用方式(事务、管道、分布式)介绍

    1.普通同步 @Test public void test1Normal() { Jedis jedis = new Jedis("localhost"); long start ...

  9. 【转载】Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍

    转载地址:http://blog.csdn.net/truong/article/details/46711045 关键字:Redis的Java客户端Jedis的八种调用方式(事务.管道.分布式…)介 ...

随机推荐

  1. Keil c中自定义带可变参数的printf函数

    在嵌入式c中,往往采用串口打印函数来实现程序的调试,而在正式程序中一般是不需要这些打印代码的,通常做法是在这些调试用打印代码的前后设置一个宏定义块来实现是否启用这段代码,比如: // other us ...

  2. 教你干掉win10全家桶

    原文: 教你干掉win10全家桶 这些并不好用的自带应用例如:groove音乐,相片,股票……一直占据着我们的默认应用.如果它们是一直静静的躺在那里还好,最多不用就是了.当我们想要浏览图片或者看视频的 ...

  3. C#基础加强篇----委托、Lamada表达式和事件(上)

    1.委托 C#的委托相当于C/C++中的函数指针.函数指针用指针获取一个函数的入口地址,实现对函数的操作. 委托与C/C++中的函数指针不同在于,委托是面向对象的,是引用类型,对委托的使用要先定义后实 ...

  4. Web页面制作之开发调试工具

    直击现场 <HTML开发MacOSApp教程>  http://pan.baidu.com/s/1jG1Q58M 开发工具介绍 开发工具一般分为两种类型:文本编辑器和集成开发环境(IDE) ...

  5. Qt for android触摸手势事件QGestureEvent

    在触摸设备上可以使用Qt的手势事件 要激活手势事件,需要执行以下操作: 第一步,为QWidget控件注册手势事件 QList<Qt::GestureType> gestures; gest ...

  6. SqlServer 动态SQL(存储过程)中Like 传入参数无正确返回值的问题

    最近在做项目时,以动态Sql进行Like语句查询时发现应该返回的结果却一直返回空,后来发现是写法错误: 错误SQL: DECLARE @0 varchar(20) SET @0 = 'XA-LZ' S ...

  7. 联想笔记本进入不了BIOS的解决方法

    当计算机遇到问题时,很多情况下需要进入BIOS进行解决.但很多新出的联想笔记本电脑在开机时,无论怎么疯狂的按F2,Fn+F2,F12或者Del,都无法进入BIOS,十分气人. 这种现象出现的原因是联想 ...

  8. C语言实现常用排序算法——基数排序

    #include<stdio.h> #include<math.h> #define SIZE 10 #define C_SIZE 20 /*行数稳定=10,切记!列数务必搞的 ...

  9. ElasticSearch学习(一):ElasticSearch介绍

    一.ElasticSearch是什么? ElasticSearch是一款非常强大的.基于Lucene的开源搜索及分析引擎,可以帮助你从海量数据中,快速找到相关的数据信息. 比如,当你在GitHub上搜 ...

  10. 系统学习 Java IO ---- 目录,概览

    Java IO 类的系统教程,原创.主要参考自英文教程 Java IO Tutorial 和 Java Doc. http://tutorials.jenkov.com/java-io/index.h ...