RocketMQ 安装使用

一、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.shrunbroker.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 的陪伴下,我们能够在分布式技术的海洋中乘风破浪,驶向更加美好的未来。

相关推荐
丘山子16 分钟前
一些鲜为人知的 IP 地址怪异写法
前端·后端·tcp/ip
CopyLower40 分钟前
在 Spring Boot 中实现 WebSockets
spring boot·后端·iphone
天天扭码1 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑2 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
追逐时光者2 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
天天扭码3 小时前
深入讲解Javascript中的常用数组操作函数
前端·javascript·面试
方圆想当图灵3 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫3 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
渭雨轻尘_学习计算机ing3 小时前
二叉树的最大宽度计算
算法·面试