【Java进阶营】从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘

基于RocketMQ分布式事务 - 完整示例 前言 之前我们说到,分布式事务是一个复杂的技术问题。没有通用的解决方案,也缺乏简单高效的手段。 不过,如果我们的系统不追求强一致性,那么最常用的还是最终一致性方案。今天,我们就基于 RocketMQ来实现消息最终一致性方案的分布式事务。 本文代码不只是简单的demo,考虑到一些异常情况、幂等性消费和死信队列等情况,尽量向可靠业务场景靠拢。 另外,在最后还有《RocketMQ技术内幕》一书中,关于分布式事务示例代码的错误流程分析,所以篇幅较长,希望大家耐心观看。 一、事务消息 在这里,笔者不想使用大量的文字赘 阅读详情

image

事务消息是RocketMQ提供的非常重要的一个特性,在4.x版本之后开源,可以利用事务消息轻松地实现分布式事务。本文对RocketMQ的事务消息进行详细介绍,并给出了代码示例。

一. 相关概念

RocketMQ在其消息定义的基础上,对事务消息扩展了两个相关的概念:

  • Half(Prepare) Message——半消息(预处理消息):半消息是一种特殊的消息类型,该状态的消息暂时不能被Consumer消费。当一条事务消息被成功投递到Broker上,但是Broker并没有接收到Producer发出的二次确认时,该事务消息就处于"暂时不可被消费"状态,该状态的事务消息被称为半消息。

  • Message Status Check——消息状态回查:由于网络抖动、Producer重启等原因,可能导致Producer向Broker发送的二次确认消息没有成功送达。如果Broker检测到某条事务消息长时间处于半消息状态,则会主动向Producer端发起回查操作,查询该事务消息在Producer端的事务状态(Commit 或 Rollback)。可以看出,Message Status Check主要用来解决分布式事务中的超时问题。

二. 执行流程

image

上面是官网提供的事务消息执行流程图,下面对具体流程进行分析:

  • Step1:Producer向Broker端发送Half Message;
  • Step2:Broker ACK,Half Message发送成功;
  • Step3:Producer执行本地事务;
  • Step4:本地事务完毕,根据事务的状态,Producer向Broker发送二次确认消息,确认该Half Message的Commit或者Rollback状态。Broker收到二次确认消息后,对于Commit状态,则直接发送到Consumer端执行消费逻辑,而对于Rollback则直接标记为失败,一段时间后清除,并不会发给Consumer。正常情况下,到此分布式事务已经完成,剩下要处理的就是超时问题,即一段时间后Broker仍没有收到Producer的二次确认消息;
  • Step5:针对超时状态,Broker主动向Producer发起消息回查;
  • Step6:Producer处理回查消息,返回对应的本地事务的执行结果;
  • Step7:Broker针对回查消息的结果,执行Commit或Rollback操作,同Step4。

三. 代码实例

本节通过一个简单的场景模拟RocketMQ的事务消息:存在2个微服务,分别是订单服务和商品服务。订单服务进行下单处理,并发送消息给商品服务,对于下单成功的商品进行减库存。

首先是订单服务:

/**
 * @Auther: ZhangShenao
 * @Date: 2019/3/27 16:44
 * @Description:使用RocketMQ事务消息——订单服务发送事务消息,然后进行本地下单,并通知商品服务减库存
 */
public class OrderService {
  public static void main(String[] args) throws Exception {
    TransactionMQProducer producer = new TransactionMQProducer();
    producer.setNamesrvAddr(RocketMQConstants.NAMESRV_ADDR);
    producer.setProducerGroup(RocketMQConstants.TRANSACTION_PRODUCER_GROUP);

    //自定义线程池,执行事务操作
    ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(20), (Runnable r) -> new Thread("Order Transaction Massage Thread"));
    producer.setExecutorService(executor);

    //设置事务消息监听器
    producer.setTransactionListener(new OrderTransactionListener());

    producer.start();

    System.err.println("OrderService Start");

    for (int i = 0;i < 10;i++){
      String orderId = UUID.randomUUID().toString();
      String payload = "下单,orderId: " + orderId;
      String tags = "Tag";
      Message message = new Message(RocketMQConstants.TRANSACTION_TOPIC_NAME, tags, orderId, payload.getBytes(RemotingHelper.DEFAULT_CHARSET));

      //发送事务消息
      TransactionSendResult result = producer.sendMessageInTransaction(message, orderId);
      System.err.println("发送事务消息,发送结果: " + result);
    }
  }
}

事务消息需要一个TransactionListener,主要进行本地事务的执行和事务回查,代码如下:

/**
 * @Auther: ZhangShenao
 * @Date: 2019/3/27 16:50
 * @Description:订单事务消息监听器
 */
public class OrderTransactionListener implements TransactionListener {
  private static final Map<String, Boolean> results = new ConcurrentHashMap<>();

  @Override
  public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
    String orderId = (String) arg;

    //记录本地事务执行结果
    boolean success = persistTransactionResult(orderId);
    System.err.println("订单服务执行本地事务下单,orderId: " + orderId + ", result: " + success);
    return success ? LocalTransactionState.COMMIT_MESSAGE : LocalTransactionState.ROLLBACK_MESSAGE;
  }

  @Override
  public LocalTransactionState checkLocalTransaction(MessageExt msg) {
    String orderId = msg.getKeys();
    System.err.println("执行事务消息回查,orderId: " + orderId);
    return Boolean.TRUE.equals(results.get(orderId)) ? LocalTransactionState.COMMIT_MESSAGE : LocalTransactionState.ROLLBACK_MESSAGE;
  }

  private boolean persistTransactionResult(String orderId) {
    boolean success = Math.abs(Objects.hash(orderId)) % 2 == 0;
    results.put(orderId, success);
    return success;
  }
}

下面是商品服务及监听器:

/**
 * @Auther: ZhangShenao
 * @Date: 2019/3/27 17:09
 * @Description:使用RocketMQ事务消息——商品服务接收下单的事务消息,如果消息成功commit则本地减库存
 */
public class ProductService {
  public static void main(String[] args) throws Exception {
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
    consumer.setNamesrvAddr(RocketMQConstants.NAMESRV_ADDR);
    consumer.setConsumerGroup(RocketMQConstants.TRANSACTION_CONSUMER_GROUP);
    consumer.subscribe(RocketMQConstants.TRANSACTION_TOPIC_NAME, "*");
    consumer.registerMessageListener(new ProductListener());
    consumer.start();
    System.err.println("ProductService Start");
  }
}

/**
 * @Auther: ZhangShenao
 * @Date: 2019/3/27 17:14
 * @Description:
 */
public class ProductListener implements MessageListenerConcurrently {
  @Override
  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
    Optional.ofNullable(msgs).orElse(Collections.emptyList()).forEach(m -> {
      String orderId = m.getKeys();
      System.err.println("监听到下单消息,orderId: " + orderId + ", 商品服务减库存");
    });
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
  }
}

分别运行OrderService和ProductService,可以看出只有事务执行成功的订单才会通知商品服务进行减库存。

监听到下单消息,orderId: f25a7127-307e-45ce-8f83-6e0a922ebb94, 商品服务减库存
监听到下单消息,orderId: d960171d-97c0-4e13-aa4a-c2b96102de4b, 商品服务减库存
监听到下单消息,orderId: 63aedaa2-ce74-4cb7-bf58-fb6a73082a73, 商品服务减库存
监听到下单消息,orderId: 25764461-70b2-44db-8296-960211179e6e, 商品服务减库存
监听到下单消息,orderId: fb319fe7-c8be-4edf-ae4e-6108898068ca, 商品服务减库存
监听到下单消息,orderId: 4f61a61a-7254-458a-bc10-9d4006a9f581, 商品服务减库存
RocketMQ系列6——事务消息 RocketMQ在4.3.0版中支持分布式事务消息,这里RocketMQ的事务消息是采用2PC(两段式协议) +补偿机制(消息回查)的分布式事务功能。提供消息发送与业务落库的一致性。 阅读详情

相关推荐

深度解析:基于 RocketMQ 实现分布式事务的技术实践与原理探究

half消息即半消息,和普通消息的区别是该消息不会立马被消费者消费,原因是half消息的存在是为了保证生产者本地事务和消费者的原子性和一致性,其过程如上文所介绍,初始发送的half消息是存储在MQ一个内存队列中(并未投递到topic中),只有生产者本地事务成功并发送commit通知后,这个消息才会被持久化到commitLog同时提交到topic队列中,此时消费者才能够消费该消息并执行本地事务。

禅与计算机程序设计的艺术 4963

RocketMQ事务消息

RocketMQ 事务消息

qq_42877546的博客 1万+

什么是深度展开网络?如何将迭代算法的每一次迭代展开成深度网络对应的一层?深度展开网络进行端对端训练,其中端对端是什么意思?

在深度学习和机器学习的背景下,“端到端”(End-to-end)训练指的是将输入数据直接映射到最终输出的过程,而不需要任何中间的手动数据预处理或特征工程。- **自然语言处理**:端到端自然语言处理系统可以将自然语言输入直接转化为计算机可理解的语言输出,而不需要对语言进行分解和转化。- **推荐系统**:通过使用深度神经网络,端到端推荐系统可以直接根据用户历史行为预测其未来的兴趣爱好,并推荐相应的产品或服务。4. **直接映射**:从输入到输出的直接映射,中间没有任何拆分或分解的过程。

m0_67897882的博客 5433

RocketMQ学习(五):分布式事务

Springboot RocketMQ实现分布式事务

仔哥学编程 1598

Workbench中直接调用ICEM CFD进行网格划分

自从ANSYS 12.0之后,ICEM CFD就从Workbench中被分离出去,作为一个独立的程序使用了。取而代之的是Meshing模块。 在Meshing的属性节点菜单中右键点击Mesh,选择Insert > Method,插入方法。 选择需要划分网格的几何体,点击apply。此时Geometry显示为1 Body。 设置Method为MultiZone,如果不设置成这个的话,找不到进入ICEM CFD的入口。如果要划分四面体,可以在Mapped Mesh Type中选择其他类型。

Yan的博客 6511

Win11 C盘爆满别慌!手把手教你安全迁移AppData和用户文件夹(附一键脚本)

本文针对Win11 C盘空间不足问题,提供了一套安全有效的解决方案。通过深入解析用户目录结构,指导用户利用符号链接和Robocopy工具,将AppData、下载文档等占用大户迁移至其他分区,并附带了自动化脚本,从根本上实现C盘瘦身,同时确保系统稳定和数据安全。

weixin_29231221的博客 2087

面试官:RocketMQ是什么,它有什么特性与使用场景?

面试官:RocketMQ你很精通吗?

真正的大师永远怀着一颗学徒的心 7260

使用菱FX5U系列PLC实现以太网通信的具体方法示例

接下来,我们将创建一个简单的CPU通信功能,以实现与其他设备的以太网通信。在上述代码中,我们首先设置访问号,然后创建一个数据读取命令(M_READ)和一个数据写入命令(M_WRITE)。此外,在实际应用中,您还需要确保PLC和其他设备之间的网络连接正常,并且遵循相应的通信协议。在上述代码中,我们执行了数据读取命令,并将接收到的响应存储在M_RESPONSE变量中。通过简单CPU通信功能,菱FX5U系列PLC可以实现与其他设备的以太网通信。我们将提供相应的源代码和详细的说明,以帮助您轻松实现这一功能。

DevCyberX的博客 1525

最详细的docker安装rocketMQ教程来了

RocketMQ是一款分布式、队列模型的消息中间件,是由阿里巴巴设计的,具有以下特点: 支持严格的消息顺序 支持Topic与Queue两种模式 亿级消息堆积能力 比较友好的分布式特性 同时支持Push与Pull方式消费消息 历经多次天猫双十一海量消息考验 RocketMQ是纯java编写,基于通信框架Netty。 ...

小泽 6210

ESP32 物联网宠物喂食器:实现远程智能投喂的完整方案

本项目基于 ESP32 实现了智能宠物喂食器的核心功能,硬件结构简单、成本较低,软件支持远程控制和状态监测。添加摄像头:通过 ESP32-CAM 模块实时观察宠物进食情况。APP 集成:开发专属手机 APP,支持定时设置、报警推送、喂食记录查询。语音控制:结合 Alexa 或小爱同学,实现语音指令喂食。太阳能供电:户外使用时,通过太阳能板为设备供电,节能环保。

weixin_42300449的博客 1274

【QNX+Android虚拟化方案】129 - USB眼图参数配置

【QNX+Android虚拟化方案】129 - USB眼图参数配置

|~~~热爱生活、努力学习的小伙汁~~~| 821

Docker以挂载方式安装RocketMQ

在 Docker 中安装和使用 RocketMQ 有多种方式,其中一种方式就是通过挂载本地文件的方式安装 RocketMQ。挂载的方式可以避免重启Docker镜像后,原配置丢失的问题。现在,可以使用浏览器访问 http://[your_server_ip]:9999/来访问控制台。第一次安装RocketMQ时难免踩坑,但是请保持平和心态对待,不要担心,自己遇到的问题,别人一定遇到过,肯定有解决方案。如果你还没有安装 Docker,请先安装。创建一个新的容器并指定 RocketMQ 的镜像。

weixin_43811057的博客 1362

Hermes 教程 03:Skills 系统

摘要:Hermes Skills是一种自我改进机制,可将解决问题的经验保存为可复用技能文档。v0.13.0版本遵循agentskills.io开放标准,与Claude Skills互通。技能创建流程包括发现问题、创建技能文件、加载使用和优化更新四个阶段。系统提供技能检索和自维护机制(Curator),支持技能媒体路由和多种管理命令。内置技能涵盖开发调试、创意头脑风暴等场景,通过积累技能使Hermes更适应用户需求。

程序猿_三产 728

【Spring Cloud Alibaba】RocketMQ(一)

MQ:MessageQueue,消息队列。是在互联⽹中使⽤⾮常⼴泛的⼀系列服务中间件。这个词可以分两个部分来看,⼀是Message:消息。消息是在不同进程之间传递的数据。这些进程可以部署在同⼀台机器上,也可以分布在不同机器上。⼆是Queue:队列。队列原意是指⼀种具有FIFO(先进先出)特性的数据结构,是⽤来缓存数据的。对于消息中间件产品来说,能不能保证FIFO特性,尚值得考量。但是,所有消息队列都是需要具备存储消息,让消息排队的能⼒。

Blue_Pepsi_Cola的博客 232

RK3588 部署 YOLOv5s 实战:多线程加速从 16FPS 飙升到 120FPS(含性能优化)

因为RK3588包含了3个NPU核心,支持3个核心同时工作、双核合作、以及单核工作。我们可以使用线程池来加速。线程池是一种长街的并发编程模型,它用于管理和复用线程,以提高多线程程序的性能和效率。线程池通常包含以下主要组件:1、任务队列:任务队列用于存储待执行的任务。这些任务可以是函数、方法或其他需要多线程环境中执行的操作。2、线程池管理:线程池管理器负责创建、销毁和管理线程。它会监视任务队列中的任务,根据需要创建线程,执行任务,并在任务完成后将线程放回池中以供重用。

FJN110的博客 99

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

RocketMQ源码详解系列1 《源码调试环境搭建》

1.今天我们来搭建下rocket源码调试环境。 windows eclipse + jdk8 + maven 2.下载源码 https://github.com/apache/rocketmq 3.在eclispe中导入maven项目 rocketmq-master 4.新增conf目录 (D:\dev\java_workspace\rocketmq\conf) 把如下4个配置文件放入到D:\d...

吉他程序员的博客 451

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

docker选择安装位置_docker安装rocketMQ教程(最详细)

RocketMQ是一款分布式、队列模型的消息中间件,是由阿里巴巴设计的,具有以下特点:支持严格的消息顺序支持Topic与Queue两种模式亿级消息堆积能力比较友好的分布式特性同时支持Push与Pull方式消费消息历经多次天猫双十一海量消息考验RocketMQ是纯java编写,基于通信框架Netty。其实小编就冲着RocketMQ历经多次天猫双十一海量消息考验,就感觉应该选择他了,再加上有亿级消息堆...

weixin_35728089的博客 1159

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

网易严选Java开发三面面经:大牛自我总结500页“Java成长笔记”

前言 消息中间件是分布式系统中的重要组件,在实际工作中常用消息中间件进行系统间数据交换,从而解决应用解耦、异步消息、流量削峰等问题,实现高性能、高可用、可伸缩和最终一致性架构。目前市面上可供选择的消息中间件有RabbitMQ、ActiveMQ、 Kafka、 RocketMQ、ZeroMQ、MetaMQ等。 本文总结了近年来在实际项目中使用消息中间件的经历和踩过的一些坑,可为你**在实际工作中进行产品选型、业务场景方案制定、性能调整等提供明确的思路。**由于是基于Java语言开发,因此它非常适合Java初中

m0_57286622的博客 134

RocketMQ(三)——HelloWorld

声明: 本文转自:RocketMQ(三)——HelloWorld 目录: 1.代码示例 2.代码阐释 3.运行效果 4.内容补充 讲个9·3阅兵时程序员间流传的笑话: 同学们,现在向我们走来的是程序员方阵!他们穿着拖鞋,披着毛巾,左手拿着键盘,右手举着鼠标,腋下夹着USB转换器。他们因睡眠不足而显得精神不振,喊着微弱的口号走过主席台,主席问候:程序员们辛苦了!程序员方队异口同声地答道:...

li12412414的博客 438

Windows 10下RocketMQ 5.0.0保姆级安装教程(含常见错误解决方案)

本文提供了Windows 10系统下RocketMQ 5.0.0的详细安装与配置指南,涵盖环境准备、安装步骤、常见错误解决方案及生产级优化建议。帮助开发者快速部署高性能消息队列服务,特别适合本地开发环境搭建。

weixin_30329623的博客 555

rocketMq 配置部署(二)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34175509的博客 151

消息中间件

消息中间件出现的**背景**:由于父工程模块下有多个子模块,而且子模块之间的调用关系很多,尤其商家 模块涉及的关系最多,其中商家模块用到了商品服务、广告内容服务、搜索服务、页面生成服务。这些模块之间的依赖关系成为耦合,耦合越多,维护越多,维护就越困难。由此 为了改善系统模块之间的调用关系、减少模块之间的耦合,就产生了消息中间件。 消息队列是分布式系统中的重要组件,主要是解决应用耦合、异步消息、流量...

weixin_44629187的博客 142

RocketMQ 第二章 项目实战 4 下单业务 4.3 测试

RocketMQ 第二章 项目实战 4 下单业务 4.3 测试

代码改变世界 396

终极游戏手柄映射指南:用AntiMicroX让任何游戏支持手柄操作

还在为PC游戏不支持手柄而烦恼吗?AntiMicroX就是你需要的终极解决方案!这款开源免费的游戏手柄映射工具,能让任何不支持手柄的游戏瞬间获得完整的手持操作体验。无论你是想用手柄重温经典老游戏,还是希望获得更舒适的游戏操作方式,AntiMicroX都能帮你实现。 ## 🎮 为什么你需要AntiMicroX手柄映射工具? 想象一下这样的场景:你心爱的老游戏只支持键盘操作,但你想躺在沙发上用手

gitblog_00334的博客 482
上一篇: 【Java进阶营】码农必备干活:Java编码——高效代码汇总小技巧
下一篇: Java代码混淆工具:JavaObfuscator介绍
普通网友
博客等级 码龄5年 4716粉丝 579原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值