理论部分在https://www.jianshu.com/p/453c6e7ff81c中的 “三、事务消息”。下面从代码层面看一下rockemq的事务消息

一、事务消费端。

  从代码中看到跟其他模式的消费端没有什么两样。

package org.hope.lee.consumer.transaction;

import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.common.message.MessageExt; import java.io.UnsupportedEncodingException;
import java.util.List; public class ConsumerTransaction {
public ConsumerTransaction() {
String group_name = "transaction_consumer";
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group_name);
consumer.setNamesrvAddr("192.168.31.xxx:9876;192.168.31.xxx:9876");
try {
consumer.subscribe("TopicTransaction", "*");
consumer.registerMessageListener(new Listener());
consumer.start();
} catch (MQClientException e) {
e.printStackTrace();
} } class Listener implements MessageListenerConcurrently{ public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) { try {
for(MessageExt msg : list) {
String topic = msg.getTopic();
String msgBody = new String(msg.getBody(), "utf-8");
String tags = msg.getTags();
System.out.println("收到消息:" + "topic:" + topic + ", tags:" + tags + ",msg:" + msgBody);
msg.getTags();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
} return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
public static void main(String[] args) {
ConsumerTransaction c = new ConsumerTransaction();
System.out.println("transaction consumer start......");
}
}

二、本地事务的执行器,实现 LocalTransactionExecuter。

package org.hope.lee.producer.transaction;

import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;
import com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.alibaba.rocketmq.common.message.Message; import java.util.concurrent.atomic.AtomicInteger; public class TransactionExecuterImpl implements LocalTransactionExecuter{
private AtomicInteger transactionIndex = new AtomicInteger(1);
public LocalTransactionState executeLocalTransactionBranch(Message message, Object o) {
System.out.println("msg = " + new String(message.getBody()));
System.out.println("o = " + o);
String tag = message.getTags();
if(tag.equals("Transaction3")) {
//这里有一个分阶段提交任务的概念
System.out.println("这里处理业务逻辑,比如操作数据库,失败情况下进行ROLLBACK"); return LocalTransactionState.ROLLBACK_MESSAGE;
}
return LocalTransactionState.COMMIT_MESSAGE;
// return LocalTransactionState.ROLLBACK_MESSAGE;
// return LocalTransactionState.COMMIT_MESSAGE.UNKNOW;
}
}

三、事务Producer端

  在这里可以看到我们用了new TransactionMQProducer()。并且在发送消息的时候添加了事务执行器producer.sendMessageInTransaction(msg, transactionExecuter, "tq")

package org.hope.lee.producer.transaction;

import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.producer.*;
import com.alibaba.rocketmq.common.message.Message;
import com.alibaba.rocketmq.common.message.MessageExt; import java.util.concurrent.TimeUnit; public class ProducerTransaction {
public static void main(String[] args) throws MQClientException {
String group_name = "transaction_producer";
//这里使用TransactionMQProducer
final TransactionMQProducer producer = new TransactionMQProducer(group_name);
producer.setNamesrvAddr("192.168.xx.xxx:9876;192.168.xx.xxx:9876");
//事务最小并法数
producer.setCheckThreadPoolMinSize(5);
//事务最大并发数
producer.setCheckThreadPoolMaxSize(20);
//队列数
producer.setCheckRequestHoldMax(2000);
/**
* Producer对象在使用之前必须要调用start()初始化,初始化一次即可
* 注意:切记不可以在每次发送消息时,都调用start()
*/
producer.start();
//服务器回调Producer,检查本地事务分支成功还是失败
//rocketmq会定时的调用这个checklistener,
//在这里,我们可以根据由MQ回传的key去数据库查询,
//判断这条数据到底是成功了还是失败了。
//就是在这个定时check,rocketmq把这个功能在开源的代码中去除掉了。
producer.setTransactionCheckListener(new TransactionCheckListener() {
public LocalTransactionState checkLocalTransactionState(MessageExt messageExt) {
System.out.println("key: " + messageExt.getKeys());
System.out.println("state--" + new String(messageExt.getBody()));
// return LocalTransactionState.ROLLBACK_MESSAGE;
return LocalTransactionState.COMMIT_MESSAGE;
// return LocalTransactionState.UNKNOW;
}
});
/**
* 下面这段代码表明一个Producer对象可以发送多个topic, 多个tag的消息
* 注意:send方法是同步调用,只要不抛异常就标识成功。但是发送成功也可会有多种状态
* 例如消息写入Master成功,但是Slave不成功,这种情况消息属于成功,但是对于个别应用
* 如果对消息可靠性要求很高需要对这种情况做处理。另外,消息可能会存在发送失败的情况
* 失败重试由应用来处理
*/
TransactionExecuterImpl transactionExecuter = new TransactionExecuterImpl();
for(int i = 1; i <= 3; i++) {
Message msg = new Message("TopicTransaction", "Transaction" + i, "key",
("Hello Rocket" +
i).getBytes());
SendResult result = producer.sendMessageInTransaction(msg, transactionExecuter, "tq");
System.out.println(result); try {
TimeUnit.MICROSECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 应用退出时,要调用shutdown来清理资源,关闭网络连接,从MetaQ服务器上注销自己
* 注意:我们建议应用在JBOSS,Tomcat等容器的退出钩子里调用shutdown 方法
*/
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
producer.shutdown();
}
})); System.exit(0);
}
}

https://gitee.com/huayicompany/RocketMQ-learn/tree/master/rocketmq-api

实验步骤:

1、先运行Consumer端

2、运行Producer端,然后看Consumer端和Produer端的控制台输出。

Consumer端Console

收到消息:topic:TopicTransaction, tags:Transaction1,msg:Hello Rocket1
收到消息:topic:TopicTransaction, tags:Transaction2,msg:Hello Rocket2

Producer端Console

msg = Hello Rocket1
o = tq
SendResult [sendStatus=SEND_OK, msgId=C0A81FB000002A9F0000000000003CDC, messageQueue=MessageQueue [topic=TopicTransaction, brokerName=broker-a, queueId=0], queueOffset=0]
msg = Hello Rocket2
o = tq
SendResult [sendStatus=SEND_OK, msgId=C0A81FB000002A9F0000000000003DA3, messageQueue=MessageQueue [topic=TopicTransaction, brokerName=broker-a, queueId=1], queueOffset=0]
msg = Hello Rocket3
o = tq
这里处理业务逻辑,比如操作数据库,失败情况下进行ROLLBACK
SendResult [sendStatus=SEND_OK, msgId=C0A81FB000002A9F0000000000003FF8, messageQueue=MessageQueue [topic=TopicTransaction, brokerName=broker-a, queueId=2], queueOffset=0]

从输出结果来看,第三条消息并没有被Consumer端消费,被回滚了。

四、RocketMQ完整版事务

  虽然rocketmq 3.2.6版本把事务部分给阉割了。但是我们还是可以了解一下其原理的。

1.0、 Producer发送消息到RocketMQ,这条消息我们暂且称之为message1 并且是transaction状态的事务消息。

1.1、MQ把message1存入数据库,并且是状态是prepared。

1.2、RocketMQ回调Producer中的本地事务。(本地事务由三个状态COMMIT_MESSAGE、ROLLBACK_MESSAGE、UNKNOW)。

1.2.1 、本地事务处理完成后,无论成功还是失败都会有一个状态,如果成功的话,Producer就会发送COMMIT_MESSAGE状态表示确认消息到RocketMQ上。

1.2.2、然后把message1这个消息存储到consumer queue中,并在数据库中把这条prepared的消息标记为commited。

1.2.3、这条消息就被Consumer消费了。

1.3.1、如果Producer的事务处理返回了一个UNKNOW状态。因为broker会定时的去扫描数据库,如果数据库中的数据状态是commited的,那么就清除这条数据。

1.4、如果数据库中数据的状态还是prepared的。那MQ就会主动的去调用Producer中的check方法。

1.5.1、check方法再去查本地的数据库看有没有减钱,如果没减钱的话就rollback,

1.6.1、rollback后Producer又发了一条ROLLBACK_MESSAGE给MQ。

1.7.1、MQ收到这条消息后,就会把MQ的数据库中对应的prepared数据给清除掉。那么这条数据也就不会被Consumer端消费了

1.5.2、check方法查本地数据库看有没有减钱,如果减钱了。

1.6.2、会给MQ发送一个COMMIT_MESSAGE。

1.7.2、MQ还会去查自己的数据库,然后把数据库中对应的数据给清除掉

RocketMQ-事务消费的更多相关文章

  1. 【转】RocketMQ事务消费和顺序消费详解

    RocketMQ事务消费和顺序消费详解 转载说明:该文章纯转载,若有侵权或给原作者造成不便望告知,仅供学习参考. 一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消 ...

  2. RocketMQ事务消费和顺序消费详解

    一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一.创建订单 ,第二:订单付款,第三:订单完成. ...

  3. RocketMQ的顺序消费和事务消费

    一.三种消费 :1.普通消费 2. 顺序消费 3.事务消费 1.1  顺序消费:在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一.创建订单 ,第二:订单付款,第三:订单完成.也就是这个三个 ...

  4. RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)

    在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...

  5. RocketMQ事务消息实现分析

    这周RocketMQ发布了4.3.0版本,New Feature中最受关注的一点就是支持了事务消息: 今天花了点时间看了下具体的实现内容,下面是简单的总结. RocketMQ事务消息概要 通过冯嘉发布 ...

  6. RocketMQ 事务消息

    RocketMQ 事务消息在实现上充分利用了 RocketMQ 本身机制,在实现零依赖的基础上,同样实现了高性能.可扩展.全异步等一系列特性. 在具体实现上,RocketMQ 通过使用 Half To ...

  7. rocketmq事务消息

    rocketmq事务消息 参考: https://blog.csdn.net/u011686226/article/details/78106215 https://yq.aliyun.com/art ...

  8. 搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务

    搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 初步认识RocketMQ的核心模块 rocketmq模块 rocketmq-broker:接受生产者发来的消息并存储(通过调用rocke ...

  9. rocketmq事务消息入门介绍

    说明 周五的时候发了篇:Rocketmq4.3支持事务啦!!!,趁着周末的时候把相关内容看了下,下面的主要内容就是关于RocketMQ事务相关内容介绍了. 说明: 今天这篇仅仅是入门介绍,并没有涉及到 ...

  10. RocketMQ事务消息学习及刨坑过程

    一.背景 MQ组件是系统架构里必不可少的一门利器,设计层面可以降低系统耦合度,高并发场景又可以起到削峰填谷的作用,从单体应用到集群部署方案,再到现在的微服务架构,MQ凭借其优秀的性能和高可靠性,得到了 ...

随机推荐

  1. TurnipBit开发板“趣味赛”:平衡力大比拼

    让孩子在快乐自由的游戏中培养编程思维 平衡力大挑战是我们经常经常玩的的一个小游戏,脑补画面的话比较常见的是单腿平衡力大比拼,摇晃幅度小者胜利.游戏好玩归好玩,但是想要公平判断胜负却不容易.下面就教大家 ...

  2. JavaWeb项目中获取对Oracle操作时抛出的异常错误码

    最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...

  3. fastdfs集群

    高可用的两大目的:数据备份,数据分片 1.FastDFS安装配置 先配置一台,将其中的配置文件打包,下载,然后配置其他机器时只需要解压即可, 打包命令 然后下载,上传到其他机器相对应的/etc目录下 ...

  4. iOS 动画篇 (二) CAShapeLayer与CoreAnimation结合使用

    接上一篇博客 iOS 动画篇(一) Core Animation CAShapeLayer是CALayer的一个子类,使用这个类能够很轻易实现曲线的动画. 先来一个折线动画效果: 示例代码: //1. ...

  5. JAVA读取和写入properties文件

    1.读取 Properties prop = new Properties(); try { //这个getResourceAsStream方法就是把文件转为inputStream的方式 prop.l ...

  6. asp.net mvc 记录Action耗时

    可能有些时候需要记录Action的执行时间来优化系统功能,这时可以用过滤器来实现. 新建项目 项目名称随便取 身份验证:不进行身份验证 安装Nlog 这里使用NLog来输出日志,具体使用说明请看:ht ...

  7. Chris Richardson微服务翻译:微服务架构中的服务发现

    Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现(本文) 微服务之事件驱动的数据管理 微服 ...

  8. flask动态url规则

    动态URL规则 URL规则可以添加变量部分,也就是件更符合同规则的URL抽象成一个URL模式. @app.route('/item/<id>') def item(id): return ...

  9. BZOJ 1002: [FJOI2007]轮状病毒【生成树的计数与基尔霍夫矩阵简单讲解+高精度】

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5577  Solved: 3031[Submit][Statu ...

  10. [51nod1610]路径计数

    路径上所有边权的最大公约数定义为一条路径的值. 给定一个有向无环图. T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模). Inp ...