一、RocketMQ 初相识
在分布式系统的广袤天地中,RocketMQ 宛如一颗璀璨的明星,作为一款高性能的分布式消息队列,它肩负着至关重要的使命。其核心价值在于为分布式系统提供了强大的解耦能力,犹如在复杂的系统架构中搭建了一座座桥梁,让各个组件能够独立运作,互不干扰。同时,在面对流量高峰时,RocketMQ 展现出卓越的削峰填谷能力,能够平稳地处理大量突发请求,确保系统的稳定性与可靠性。
想象一下,在电商系统中,当一场盛大的促销活动开启,瞬间涌入的海量订单信息如同汹涌的潮水。此时,RocketMQ 便挺身而出,它将这些订单消息迅速收集并存储起来,然后按照系统能够承受的速度,逐步将消息传递给后续的处理模块。这样一来,不仅避免了因瞬间高流量导致系统崩溃的风险,还使得各个业务模块能够专注于自身的核心任务,极大地提高了系统的整体性能和可扩展性。 正是凭借这些出色的特性,RocketMQ 在分布式系统领域中占据了不可或缺的地位,成为众多开发者构建高性能、高可靠性系统的首选工具。
二、RocketMQ 的安装之旅
(一)安装前奏曲
在开启 RocketMQ 的安装征程之前,我们首先要确保系统环境满足一系列要求。就如同建造高楼大厦需要坚实的地基一样,RocketMQ 的稳定运行依赖于合适的基础环境。
RocketMQ 对 Java Development Kit(JDK)有着明确的要求,必须为 64 位的 JDK 1.8 及以上版本 。这是因为 RocketMQ 作为一款基于 Java 开发的消息队列,其底层的运行逻辑和性能优化都与 JDK 的版本和特性紧密相关。若 JDK 版本过低,可能无法支持 RocketMQ 的某些高级功能,甚至导致安装和运行过程中出现各种兼容性问题。
操作系统方面,RocketMQ 支持多种常见的 64 位系统,如 Linux、Unix、Mac 以及 Windows 。不同的操作系统在系统架构、文件管理、进程调度等方面存在差异,但 RocketMQ 都进行了针对性的适配,以确保在各种环境下都能稳定运行。不过,在实际选择操作系统时,需要综合考虑项目的整体架构、服务器资源以及运维团队的熟悉程度等因素。例如,在大型企业级项目中,Linux 系统因其稳定性、高效性以及丰富的开源工具生态,往往成为首选;而在开发测试环境中,Windows 系统则因其操作的便捷性,受到开发者的青睐。
(二)下载与解压
当我们的系统环境准备就绪后,便可以着手下载 RocketMQ 的安装包了。RocketMQ 的官方网站(rocketmq.apache.org/download/ )是获取安装包的权威来源,在这里,我们能够找到最新版本的 RocketMQ。
在下载页面,我们会看到 RocketMQ 提供了两种类型的安装包,一种是源代码版本,另一种是已经编译好的二进制版本。对于大多数开发者来说,二进制版本更为便捷,它免去了繁琐的编译过程,能够快速投入使用。而源代码版本则适合那些对 RocketMQ 内部机制有深入研究需求,或者需要根据特定场景对 RocketMQ 进行定制化开发的用户。
假设我们选择下载二进制版本,下载完成后,得到的是一个压缩文件,如 rocketmq-all-5.1.0-bin-release.zip 。接下来,我们需要将这个压缩文件解压到指定的目录。解压操作十分简单,在 Linux 系统中,我们可以使用命令 unzip rocketmq-all-5.1.0-bin-release.zip -d /opt/rocketmq
,将其解压到 /opt/rocketmq
目录下;在 Windows 系统中,我们可以通过解压工具,如 WinRAR 或 7-Zip,选择解压路径进行解压。解压完成后,我们便可以在指定目录下看到 RocketMQ 的文件结构,其中包含了 bin、conf、lib 等重要目录,这些目录分别存放着启动脚本、配置文件以及依赖的库文件等,它们共同构成了 RocketMQ 运行的基础。
(三)配置文件大改造
解压完成后,进入 RocketMQ 的安装目录,我们会发现一个至关重要的 conf 目录,这里存放着 RocketMQ 的各种配置文件。为了让 RocketMQ 能够更好地适应我们的系统环境和业务需求,对这些配置文件进行适当的修改是必不可少的步骤。
在这些配置文件中,启动配置文件尤为关键。以 Linux 系统为例,在 bin 目录下的 runserver.sh 和 runbroker.sh 文件分别用于启动 NameServer 和 Broker。打开 runserver.sh 文件,我们可以看到其中的 JAVA_OPT 配置项,它用于设置 Java 虚拟机的启动参数。默认情况下,RocketMQ 可能会根据自身的推荐配置设置一些参数,但在实际应用中,我们往往需要根据服务器的内存情况进行调整。比如,如果服务器的内存资源有限,我们可以将 JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn125m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m
中的初始堆内存(-Xms)和最大堆内存(-Xmx)适当减小,以避免因内存分配过大导致系统资源不足。同理,对于 runbroker.sh 文件,也需要进行类似的内存参数调整,确保 Broker 在启动时能够合理地使用内存资源 。
除了内存参数,我们还可能需要根据实际的网络环境和集群部署要求,对配置文件中的其他参数进行修改。例如,如果我们的 RocketMQ 集群需要与外部的其他系统进行通信,那么就需要在配置文件中指定正确的网络地址和端口号,以确保消息能够准确无误地传输。
(四)启动 RocketMQ
当我们完成了配置文件的修改后,就可以正式启动 RocketMQ 了。RocketMQ 的启动过程分为两个主要步骤,分别是启动 NameServer 和启动 Broker。
在启动 NameServer 时,我们进入 RocketMQ 的 bin 目录,执行命令 sh mqnamesrv
。此时,NameServer 便开始启动,为整个 RocketMQ 集群提供命名服务。NameServer 就像是一个庞大的通讯录,记录着各个 Broker 的地址和相关信息,为生产者和消费者在发送和接收消息时提供路由指引。启动成功后,我们可以通过日志来确认,如果能够看到类似 The Name Server boot success
的日志信息,那就说明 NameServer 已经成功启动。
接下来,启动 Broker。在启动 Broker 之前,我们需要确保 NameServer 已经正常运行。同样进入 bin 目录,执行命令 sh mqbroker -n localhost:9876
。这里的 -n localhost:9876
参数指定了 NameServer 的地址和端口。Broker 启动后,会连接到指定的 NameServer,并将自身的信息注册到 NameServer 中。我们可以通过查看日志来确认 Broker 是否启动成功,如果在日志中看到 The broker XXXX boot success. serializeType=JSON and name server is XXXX
的信息,XXXX
表示你的服务信息,就表明 Broker 已经顺利启动。
当 NameServer 和 Broker 都成功启动后,我们还可以通过一些方式来验证 RocketMQ 是否正常工作。例如,我们可以使用 RocketMQ 自带的命令行工具,如在 bin 目录下执行 sh mqadmin updateTopic -n localhost:9876 -t testTopic -c DefaultCluster -r 8 -w 8
创建一个主题,
然后使用 sh mqadmin topicList -n localhost:9876
命令,查看当前 RocketMQ 集群中已有的主题列表。 如果能够正常列出主题信息,那就说明 RocketMQ 已经可以正常接收和处理消息,至此,我们的 RocketMQ 安装和启动工作就圆满完成了。
三、RocketMQ 消息类型全解析
在 RocketMQ 的消息宇宙中,不同类型的消息犹如各具特色的工具,为我们解决各种复杂的业务问题提供了有力支持。接下来,让我们深入了解 RocketMQ 的几种常见消息类型。 需要提前引入 pom 文件
pom
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>5.3.1</version>
</dependency>
(一)普通消息:基础且常用
普通消息,作为 RocketMQ 中最基础的消息类型,如同建筑中的基石,承载着众多系统的基本通信需求。它的特点在于简单直接,生产者只管将消息发送至 Broker,消费者则从 Broker 拉取消息进行处理,消息之间并无特定的顺序要求。这种特性使得普通消息在许多场景中都能大显身手,尤其是在那些对消息处理顺序没有严格要求,只注重消息可靠传输的场景中。
在微服务架构中,各个微服务之间往往需要进行大量的数据交互。以一个电商系统为例,订单服务在接收到用户的下单请求后,需要将订单信息传递给库存服务、支付服务等多个下游服务。此时,使用普通消息进行解耦是非常合适的选择。订单服务将订单信息封装成普通消息发送到 RocketMQ,下游的库存服务和支付服务可以根据自身的处理能力,从 RocketMQ 中拉取消息进行处理。这样一来,即使某个下游服务出现短暂的故障或性能问题,也不会影响到订单服务的正常运行,从而实现了各个微服务之间的异步解耦,提高了系统的整体稳定性和可扩展性 。
在离线的日志收集场景中,普通消息也发挥着重要作用。通过在前端应用中埋点收集用户的操作日志,然后将这些日志数据封装成普通消息发送到 RocketMQ。RocketMQ 负责将这些消息可靠地投递到下游的存储系统和分析系统,后续的日志存储和分析工作由相应的后端应用完成。由于日志数据的处理通常不要求严格的顺序,普通消息能够高效地完成日志数据的传输任务,为系统的运维和数据分析提供了有力支持。
首先创建一个普通消息topic
shell
sh mqadmin updateTopic -n localhost:9876 -t GeneralTopic -c DefaultCluster -r 8 -w 8
sh mqadmin updateTopic
:使用mqadmin
工具的updateTopic
子命令,用于更新或创建 Topic。-n localhost:9876
:指定 NameServer 的地址和端口,这里使用localhost:9876
,你可以根据实际情况修改为你的 NameServer 的地址。-t GeneralTopic
:指定要创建的 Topic 名称,这里是GeneralTopic
,可根据需求修改。-c DefaultCluster
:指定集群名称,DefaultCluster
是一个示例,可以替换为实际使用的集群名称。-r 8
:设置读队列的数量为 8,你可以根据实际情况调整此参数。-w 8
:设置写队列的数量为 8,同样可根据实际情况调整。
下面,我们通过一段 Java 代码示例来看看普通消息的发送和接收过程:
java
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
public class Producer {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
// 创建一个生产者实例,指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("GeneralTopicProducerGroup");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动生产者
producer.start();
// 创建一条消息,指定主题、标签和消息体
Message message = new Message("GeneralTopic", "*", "Hello, RocketMQ!".getBytes());
// 发送消息并获取发送结果
SendResult sendResult = producer.send(message);
System.out.println(sendResult);
// 关闭生产者
producer.shutdown();
}
}
java
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
public class Consumer {
public static void main(String[] args) throws MQClientException {
// 创建一个消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GeneralTopicConsumerGroup");
// 设置NameServer的地址
consumer.setNamesrvAddr("localhost:9876");
// 订阅主题
consumer.subscribe("GeneralTopic", "*");
// 注册消息监听器,处理接收到的消息
consumer.registerMessageListener((MessageListenerConcurrently) (messageList, context) -> {
for (MessageExt msg : messageList) {
System.out.println(new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
// 启动消费者
consumer.start();
}
}
(二)顺序消息:有序的保障
在某些业务场景中,消息的顺序性至关重要。例如在订单处理系统中,订单的创建、支付、发货等操作必须严格按照顺序进行,否则可能会导致业务逻辑混乱。这时,顺序消息便成为了我们的得力助手。
顺序消息的核心原理在于,通过将具有相同 "分区键"(通常是业务相关的唯一标识符,如订单 ID)的消息发送到同一个队列中,而队列本身是按照先进先出(FIFO)的顺序进行存储和投递的,从而确保了消费者能够按照消息的发送顺序进行消费。在一个电商订单处理系统中,对于同一订单的所有相关消息,如订单创建消息、支付成功消息、发货消息等,都可以使用该订单的 ID 作为分区键。这样,这些消息都会被发送到同一个队列中,消费者从该队列中拉取消息时,就能够保证按照订单处理的正确顺序依次处理这些消息,避免了因消息乱序而导致的业务错误 。
在金融交易系统中,股票买卖、资金划转等操作也需要严格按照顺序执行。以股票交易为例,先买入股票再卖出股票的顺序是不能颠倒的。通过使用顺序消息,将同一股票账户的所有交易消息发送到同一个队列,消费者按照顺序处理这些消息,就能确保交易的一致性和准确性,有效避免了因消息顺序错误而引发的交易风险。
首先创建一个顺序消息topic
shell
sh mqadmin updateTopic -n localhost:9876 -t OrderTopic -c DefaultCluster -r 8 -w 8 -o true
sh mqadmin updateTopic
:使用mqadmin
工具的updateTopic
子命令。-n localhost:9876
:指定 NameServer 的地址和端口,这里是localhost:9876
。-t OrderTopic
:指定要创建的 Topic 名称,这里是OrderTopic
。-c DefaultCluster
:指定集群名称,这里是DefaultCluster
,你可以根据实际情况修改为你所使用的集群名称。-r 8
:设置读队列的数量为 8。-w 8
:设置写队列的数量为 8。-o true
:将order
参数设置为true
,表示该 Topic 是一个支持顺序消息的 Topic。
下面是一个使用 Java 代码实现顺序消息发送和接收的示例:
java
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.util.List;
public class Producer {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
// 创建一个生产者实例,指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("OrderTopicProducerGroup");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动生产者
producer.start();
// 订单ID
String orderId = "123456";
// 创建一条消息,指定主题、标签和消息体
Message message = new Message("OrderTopic", "CreateOrder", ("创建订单:" + orderId).getBytes());
// 发送消息,使用MessageQueueSelector确保消息发送到同一队列
SendResult sendResult = producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String id = (String) arg;
// 这里简单地根据订单ID取模选择队列,确保同一订单的消息在同一队列
int index = Math.abs(id.hashCode()) % mqs.size();
return mqs.get(index);
}
}, orderId);
System.out.println(sendResult);
// 关闭生产者
producer.shutdown();
}
}
java
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
public class Consumer {
public static void main(String[] args) throws MQClientException {
// 创建一个消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("OrderTopicConsumerGroup");
// 设置NameServer的地址
consumer.setNamesrvAddr("localhost:9876");
// 订阅主题
consumer.subscribe("OrderTopic", "*");
// 注册顺序消息监听器
consumer.registerMessageListener((MessageListenerOrderly) (messageList, context) -> {
for (MessageExt msg : messageList) {
System.out.println(new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
});
// 启动消费者
consumer.start();
}
}
(三)定时 / 延时消息:精准的定时任务
在实际业务中,我们常常会遇到需要定时执行某些任务的场景,比如定时发送邮件通知、定时清理过期数据等。RocketMQ 的定时 / 延时消息就为我们提供了一种便捷的解决方案。
定时 / 延时消息允许我们在发送消息时指定一个延迟时间或定时时间,消息在到达指定时间后才会被消费者消费。这种特性使得它在任务调度、定时提醒等场景中有着广泛的应用。在电商系统中,对于用户下单后一段时间未支付的订单,我们可以发送一条定时消息,在订单创建后的 30 分钟触发。当消费者接收到这条消息时,检查订单状态,如果仍未支付,则自动取消订单,将商品放回库存。这样既保证了订单的时效性,又减轻了系统的实时处理压力 。
在一些需要定时进行数据备份或系统维护的场景中,定时 / 延时消息也能发挥重要作用。例如,我们可以在每天凌晨 2 点发送一条定时消息,触发数据备份任务,确保数据的安全性和完整性。通过合理设置定时 / 延时消息,我们能够实现系统的自动化运维,提高系统的稳定性和可靠性。
首先创建一个延时消息topic
shell
sh mqadmin updateTopic -n localhost:9876 -t DelayTopic -c DefaultCluster -r 8 -w 8
sh mqadmin updateTopic
:使用mqadmin
工具的updateTopic
子命令。-n localhost:9876
:指定 NameServer 的地址和端口,这里是localhost:9876
,可根据实际情况修改。-t DelayTopic
:指定要创建的 Topic 名称,这里使用DelayTopic
作为示例,可根据需要进行替换。-c DefaultCluster
:指定集群名称,这里使用DefaultCluster
,根据实际使用的集群修改。-r 8
:设置读队列的数量为 8,可以根据实际的业务情况进行调整。-w 8
:设置写队列的数量为 8,同样可根据业务需求调整。
下面是一个使用 Java 代码发送定时 / 延时消息的示例:
java
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
public class Producer {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
// 创建一个生产者实例,指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("DelayTopicProducerGroup");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动生产者
producer.start();
// 创建一条消息,指定主题、标签和消息体
Message message = new Message("DelayTopic", "*", "定时任务消息".getBytes());
// 设置延时等级,这里假设延时等级1表示10秒后投递(具体延时时间根据RocketMQ配置)
// messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
int delayLevel = 1;
// 发送延时消息
SendResult sendResult = producer.send(message, delayLevel);
System.out.println(sendResult);
// 关闭生产者
producer.shutdown();
}
}
java
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
public class Consumer {
public static void main(String[] args) throws MQClientException {
// 创建一个消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DelayTopicConsumerGroup");
// 设置NameServer的地址
consumer.setNamesrvAddr("localhost:9876");
// 订阅主题
consumer.subscribe("DelayTopic", "*");
// 注册顺序消息监听器
consumer.registerMessageListener((MessageListenerOrderly) (messageList, context) -> {
for (MessageExt msg : messageList) {
System.out.println(new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
});
// 启动消费者
consumer.start();
}
}
在 RocketMQ 中,消费定时 / 延时消息的代码与消费普通消息的代码基本相同,消费者不需要特殊处理即可接收并处理到达时间的定时 / 延时消息。
(四)事务消息:分布式事务的利器
在分布式系统中,保证事务的一致性是一个极具挑战性的问题。RocketMQ 的事务消息为我们提供了一种有效的解决方案,它能够确保本地事务与消息发送的最终一致性。
事务消息的工作原理基于两阶段提交(2PC)和事后补偿机制。具体来说,生产者首先发送一条半事务消息(即消息已经发送到 Broker,但标记为暂不能投递),然后执行本地事务。如果本地事务执行成功,生产者向 Broker 提交事务确认,Broker 将半事务消息标记为可投递,消费者可以消费该消息;如果本地事务执行失败,生产者向 Broker 回滚事务,Broker 丢弃该半事务消息。如果 Broker 在一定时间内未收到生产者的事务确认,它会主动回查生产者的本地事务状态,生产者根据实际情况进行提交或回滚操作,从而保证了事务的最终一致性。
在电商下单场景中,当用户下单时,系统需要同时完成创建订单和扣减库存这两个操作。我们可以使用事务消息来确保这两个操作要么都成功,要么都失败。生产者首先发送一条事务消息,包含订单信息和库存扣减信息。然后执行本地事务,创建订单并尝试扣减库存。如果库存扣减成功,生产者向 Broker 提交事务确认,消费者可以接收到消息并进行后续处理,如发送订单确认邮件给用户;如果库存扣减失败,生产者回滚事务,Broker 丢弃消息,避免了因库存不足但订单已创建而导致的业务不一致问题 。
首先创建一个事物消息topic
shell
sh mqadmin updateTopic -n localhost:9876 -t TransactionTopic -c DefaultCluster -r 8 -w 8
sh mqadmin updateTopic
:使用mqadmin
工具的updateTopic
子命令。-n localhost:9876
:指定 NameServer 的地址和端口,这里是localhost:9876
,可根据实际情况修改。-t TransactionTopic
:指定要创建的 Topic 名称,这里使用TransactionTopic
作为示例,可根据实际需求更改。-c DefaultCluster
:指定集群名称,这里使用DefaultCluster
,根据使用的集群进行修改。-r 8
:设置读队列的数量为 8,可以根据业务需求调整。-w 8
:设置写队列的数量为 8,同样可按需调整。
下面是一个使用 Java 代码实现事务消息的示例:
java
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.concurrent.TimeUnit;
public class Producer {
public static void main(String[] args) throws MQClientException, InterruptedException {
// 创建一个事务生产者实例,指定生产者组名
TransactionMQProducer producer = new TransactionMQProducer("TransactionTopicProducerGroup");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 设置事务监听器
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 执行本地事务,这里简单模拟创建订单和扣减库存操作
try {
// 模拟创建订单成功
System.out.println("创建订单成功");
// 模拟扣减库存成功
System.out.println("扣减库存成功");
return LocalTransactionState.COMMIT_MESSAGE;
} catch (Exception e) {
// 本地事务执行失败,回滚事务
System.out.println("本地事务执行失败,回滚事务");
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 检查本地事务状态,这里简单返回COMMIT_MESSAGE表示事务已提交
System.out.println("检查本地事务状态,事务已提交");
return LocalTransactionState.COMMIT_MESSAGE;
}
});
// 启动生产者
producer.start();
// 创建一条消息,指定主题、标签和消息体
Message message = new Message("TransactionTopic", "*", "创建订单事务消息".getBytes());
// 发送事务消息
SendResult sendResult = producer.sendMessageInTransaction(message, null);
System.out.println(sendResult);
// 保持主线程运行,以便观察事务处理结果
TimeUnit.MINUTES.sleep(5);
// 关闭生产者
producer.shutdown();
}
}
java
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class Consumer {
public static void main(String[] args) throws MQClientException {
// 创建一个消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("TransactionTopicConsumerGroup");
// 设置NameServer的地址
consumer.setNamesrvAddr("localhost:9876");
// 订阅主题
consumer.subscribe("TransactionTopic", "*");
// 注册消息监听器,处理接收到的消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> messageList, ConsumeConcurrentlyContext context) {
for (MessageExt msg : messageList) {
System.out.println(new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 启动消费者
consumer.start();
}
}
四、总结
RocketMQ 以其卓越的性能、丰富的特性和强大的功能,在分布式架构的舞台上大放异彩。它不仅为系统提供了高效的解耦能力,确保了各模块之间的独立运作,还在流量削峰填谷、消息可靠传输等方面发挥着关键作用。无论是普通消息的广泛应用,还是顺序消息对业务逻辑的严格保障;无论是定时 / 延时消息对任务调度的精准控制,还是事务消息对分布式事务一致性的有力维护,RocketMQ 都展现出了其作为一款优秀消息队列的独特魅力。
在当今数字化浪潮汹涌澎湃的时代,分布式架构的应用场景日益广泛,RocketMQ 的发展前景也极为广阔。随着云计算、大数据、人工智能等新兴技术的不断崛起,对消息队列的性能、可靠性和扩展性提出了更高的要求。RocketMQ 凭借其不断演进的技术和强大的社区支持,有望在这些领域中持续发挥重要作用,助力企业构建更加高效、稳定、智能的分布式系统 。
希望通过本文的介绍,能够激发大家对 RocketMQ 的浓厚兴趣,鼓励大家深入探索 RocketMQ 的更多奥秘。在实际项目中,大胆尝试运用 RocketMQ 的各种特性,解决复杂的业务问题,为分布式系统的构建贡献自己的智慧和力量。相信在 RocketMQ 的陪伴下,我们能够在分布式技术的海洋中乘风破浪,驶向更加美好的未来。