RocketMQ从实战到源码:初识RocketMQ

RocketMQ从实战到源码:初识RocketMQ

😄生命不息,写作不止

🔥 继续踏上学习之路,学之分享笔记

👊 总有一天我也能像各位大佬一样

🏆 博客首页 @怒放吧德德 To记录领地 @一个有梦有戏的人

🌝分享学习心得,欢迎指正,大家一起学习成长!

转发请携带作者信息 @怒放吧德德(掘金) @一个有梦有戏的人(CSDN)

前言

接下来就是开启新一阶段的技术学习,要不断补充技能,于是我就开启了自己的 RocketMQ 技术,本次学习依旧是视频+AI+动手的方式进行学习,并持续产出文档。2025-12-30创建栏目,说要学习,一直出差,拖到现在!!!

1 RocketMQ 介绍

RocketMQ 是一个分布式、队列模型的开源消息中间件 ,由阿里巴巴团队最初开发,并于2016年捐赠给Apache软件基金会 ,2017年成为Apache顶级项目。它旨在处理海量消息的高吞吐、低延迟、高可用、高可靠的传输,是构建大型分布式系统的核心基础设施。

1.1 基本概念

  • NameServer:可以看成是注册中心,负责更新和发现 Broker 服务,在 NameServer 集群中,NameServer 之间是没有相互通信的,它是无状态的。
  • Broker:可以理解是消息中转角色,负责消息的存储与转化,接收生产者发送来的消息,并将其持久化到磁盘,根据消费者的拉取请求,将存储的消息投递出去;当用户发送消息到 Broker 时,Broker 会将消息转发到关联的 Topic 中。
1.1.1 核心组件与结构
  • 主题(Topic):消息传输和存储的顶层容器,用于标识同一类业务逻辑的消息,通过 唯一标识。
  • 消息队列(MessageQueue):消息存储和传输的实际容器,也是最小存储单元。主题由多个队列组成,通过 唯一标识。
  • 消息(Message):最小数据传输单元,包含业务数据的负载和拓展属性。
  • 消息视图(MessageView) :面向开发视角提供的一种消息只读接口,用于读取消息属性和负载信息,不能修改消息本身。
1.1.2 消息分类与属性
  • 消息类型(MessageType) :按照消息传输特性定义的分类,包括:
    • 普通消息
    • 顺序消息
    • 事务消息
    • 定时/延时消息
    • 从5.0版本开始,每个主题只允许发送一种消息类型,该强制校验功能默认开启。
  • 消息标签(MessageTag):主题层级下细粒度的消息分类属性,消费者可订阅特定标签进行过滤。
  • 消息索引(MessageKey):面向消息的索引属性,用于快速查找消息。
1.1.3 消息位置与进度
  • 消息位点(MessageQueueOffset):消息在队列中的唯一 类型坐标,按到达服务端的顺序存储。
  • 消费位点(ConsumerOffset) :每个消费者分组记录消费过的最新一条消息的位点。
  • 重置消费位点:以时间轴为坐标,重新设置消费者分组对已订阅主题的消费进度。
1.1.4 生产者相关
  • 生产者(Producer):构建并传输消息到服务端的运行实体,通常集成在业务系统中。
  • 事务检查器(TransactionChecker):生产者用于执行本地事务检查和异常事务恢复的监听器。
  • 事务状态(TransactionResolution) :事务消息发送过程中,事务提交的状态标识,包括:
    • 事务提交
    • 事务回滚
    • 事务未决
1.1.5 消费者相关
  • 消费者分组(ConsumerGroup) :承载多个消费行为一致的消费者的逻辑分组,用于实现负载均衡和高可用。
  • 消费者(Consumer):接收并处理消息的运行实体。
  • 消费结果(ConsumeResult) : 消费监听器返回的消息处理结果,包含消费成功消费失败
  • 订阅关系(Subscription):消费者获取和处理消息的规则与状态配置,包括过滤规则和消费进度维护。
1.1.6 高级功能与状态
  • 消息过滤:消费者通过订阅指定消息标签(Tag)进行过滤,过滤计算在服务端完成。
  • 消息轨迹:消息从生产到消费完整链路中各节点的时间、地点等数据,用于问题排查。
  • 消息堆积:消息已发送到服务端,但因消费者消费能力有限,未能及时消费的状态。
  • 事务消息:保障分布式场景下消息生产和本地事务最终一致性的高级消息类型。
  • 定时/延时消息:发送至服务端后,在指定时间后才能被消费的高级消息类型。
  • 顺序消息:支持消费者按照发送先后顺序获取和处理消息的高级消息类型。

1.2 RocketMQ 工作原理

官网介绍:

RocketMQ 5.0 引入了全新的弹性无状态代理模式,将当前的Broker职责进行拆分,对于客户端协议适配、权限管理、消费管理等计算逻辑进行抽离,独立无状态的代理角色提供服务,Broker则继续专注于存储能力的持续优化。这套模式可以更好地实现在云环境的资源弹性调度。 值得注意的是RocketMQ 5.0的全新模式是和4.0的极简架构模式相容相通的,5.0的代理架构完全可以以Local模式运行,实现与4.0架构完全一致的效果。开发者可以根据自身的业务场景自由选择架构部署。

2 安装 RocketMQ

2.1 下载 RocketMQ

官网:RocketMQ · 官方网站 | RocketMQ

github:GitHub - apache/rocketmq: Apache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications.

本次使用的版本是 5.3.0

2.2 安装 RocketMQ

首先需要准备一台 centos 的虚拟机或服务器,并且需要安装 jdk,具体可以看看网上教程,这里不再赘述。

2.2.1上传安装包

通过使用 shell 工具将压缩包上传到虚拟机上:/usr/rocketmq ,我懒得在弄分类了,反正是虚拟机做测试,我就是直接上传解压。

shell 复制代码
[root@localhost rocketmq]# cd /usr/rocketmq
# 如果想要管理可用以下命令
[root@localhost rocketmq]# mv rocketmq-all-5.3.0-bin-release /your/folder
[root@localhost rocketmq]# unzip rocketmq-all-5.3.0-bin-release.zip
[root@localhost rocketmq-all-5.3.0-bin-release]# ll
总用量 100
drwxr-xr-x. 2 root root   126 7月  10 2024 benchmark
drwxrwxr-x. 4 root root  4096 12月 30 00:03 bin
drwxr-xr-x. 8 root root  4096 7月  10 2024 conf
drwxr-xr-x. 2 root root  8192 7月  10 2024 lib
-rw-rw-r--. 1 root root 17327 7月   8 2024 LICENSE
-rw-------. 1 root root 44535 12月 28 13:24 nohup.out
-rw-rw-r--. 1 root root  1338 7月   8 2024 NOTICE
-rw-rw-r--. 1 root root 12266 7月   8 2024 README.md

bin:脚本目录,包含启动、停止和管理RocketMQ的各种脚本。

conf:配置文件目录,存放所有配置文件。

lib:依赖库目录,包含RocketMQ运行所需的所有JAR包,包括客户端、工具等 。

benchmark:性能测试工具

2.2.2 配置 RocketMQ 环境变量

shell 复制代码
[root@localhost rocketmq]# vi /etc/profile
# 配置rocketmq路径
export ROCKETMQ_HOME=/usr/rocketmq/rocketmq-all-5.3.0-bin-release

2.2.3 更新配置

shell 复制代码
[root@localhost rocketmq]# source /etc/profile

ROCKETMQ_HOME 的环境变量不配置的话,可能会在启动 NameServer 和 Broker 的时候报错。

2.2.4 启动服务

RocketMQ 需要启动两个,一个是 NameServer,另一个是 Broker。

① 启动 NameServer 服务

启动 NameServer 服务只需要在 rocketmq/bin 下有个吗 mqnamesrv。启动这个就行,但是其默认配置文件设置的 JVM 内存是 4G,这里我们需要修改,直接修改 runserver.sh

shell 复制代码
[root@localhost rocketmq]# cd /usr/rocketmq/rocketmq-all-5.3.0-bin-release/bin
[root@localhost bin]# vi runserver.sh

JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

改为

JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

然后启动

shell 复制代码
[root@localhost bin]# nohup ./mqnamesrv &
# 查看日志
[root@localhost bin]# tail -f nohup.out

显示 The Name Server boot success 就表示启动成功。

② 启动 Broker 服务

启动的脚本是 runbroker,但是这里默认配置内存是 8G,如果不够,需要调整一下 JVM 内存,修改 runbroker.sh 文件。

shell 复制代码
[root@localhost rocketmq]# cd /usr/rocketmq/rocketmq-all-5.3.0-bin-release/bin
[root@localhost bin]# vi runbroker.sh

JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g"

改为

JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m"

修改 broker 配置文件

shell 复制代码
[root@localhost bin]# cd /usr/rocketmq/rocketmq-all-5.3.0-bin-release/conf
[root@localhost conf]# vi broker.conf
# 允许自动创建topic
autoCreateTopicEnable=true
# 添加nameserver地址
namesrvAddr=localhost:9876

启动 broker

shell 复制代码
[root@localhost bin]# nohup ./mqbroker &
# 查看日志
[root@localhost bin]# tail -f nohup.out
[root@localhost bin]# jps

通过 jps 可以看到启动的 nameserve 和 broker 服务。

2.2.5 关闭服务

shell 复制代码
sh ./mqshutdown broker
sh ./myshutdown namesrv

3 安装 rockermq-dashboard

安装 rocketmq 可视化界面,需要自行对代码进行编译打包,下载代码包,使用 idea 打开进行打包,这个过程不赘述。

3.1 运行可视化看板

将打包好的 jar 包放到虚拟机中,直接通过 java -jar 的方式进行运行即可。

shell 复制代码
[root@localhost bin]# cd /usr/rocketmq
[root@localhost rocketmq]# java -jar rocketmq-dashboard-2.0.0.jar 1>dashboard.log 2>&1 &

启动完毕可以通过:http://192.168.109.134:8080/#/ 查看

(集群部署后续有时间补充...)

4 简单案例

可以参考源码里面的测试代码:rocketmq-example 模块下的 org.apache.rocketmq.example 包

需要先在 pom 引入坐标依赖

xml 复制代码
<dependency>
  <groupId>org.apache.rocketmq</groupId>
  <artifactId>rocketmq-client</artifactId>
  <version>5.1.0</version>
</dependency>

4.1 生产者

消息生产者的 3 种消息发送方法

  • 同步方法:等待消息返回后再继续进行下面的操作。
  • 异步方法:不等待消息返回直接进入后续流程,broker 将结果返回后调用 callback 函数,并使用 CountDownLatch 计数。
  • 单向方法:只负责发送,不管消息是否发送成功。

① 同步发送

java 复制代码
/**
 * 同步发送
 * @author: lyd
 * @date: 2025/12/29 23:51
 */
public class SyncProducer {
    public static final int MESSAGE_COUNT = 10;
    public static final String PRODUCER_GROUP = "Test01";
    public static final String DEFAULT_NAMESRVADDR = "192.168.109.134:9876";
    public static final String TOPIC = "Topic-Lyd";
    public static final String TAG = "Lyd";
    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, MQBrokerException, RemotingException, InterruptedException {
        // 使用生产者组名进行实例化
        DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);
        producer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
        producer.start();
        for (int i = 0; i < MESSAGE_COUNT; i++) {
            Message msg = new Message(TOPIC,
                    TAG,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
            );
            // 调用发送消息以将消息传递到代理之一。
            SendResult sendResult = producer.send(msg, 20 * 1000);
            System.out.printf("发送成功%s%n", sendResult);
        }
        /*
         * 当生产者实例不再使用时关闭。
         */
        producer.shutdown();
    }
}

如下就代表发送成功

② 异步发送

java 复制代码
/**
 * @author: lyd
 * @date: 2025/12/30 00:09
 */
public class AsyncProducer {
    public static final int MESSAGE_COUNT = 10;
    public static final String PRODUCER_GROUP = "Test02";
    public static final String DEFAULT_NAMESRVADDR = "192.168.109.134:9876";
    public static final String TOPIC = "Topic-Lyd";
    public static final String TAG = "Lyd";
    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, MQBrokerException, RemotingException, InterruptedException {
        // 使用生产者组名进行实例化
        DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);
        producer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
        producer.start();
        CountDownLatch countDownLatch = new CountDownLatch(MESSAGE_COUNT);
        for (int i = 0; i < MESSAGE_COUNT; i++) {
            Message msg = new Message(TOPIC,
                                      TAG,
                                      ("异步发送:Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
                                     );
            // 调用发送消息以将消息传递到代理之一。
            producer.send(msg, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    countDownLatch.countDown();
                    System.out.printf("异步发送成功%s%n", sendResult);
                }
                @Override
                public void onException(Throwable throwable) {
                    countDownLatch.countDown();
                    System.out.printf("异步发送失败%s%n", throwable);
                }
            });
        }
        countDownLatch.await(5, TimeUnit.SECONDS);
        /*
         * 当生产者实例不再使用时关闭。
         */
        producer.shutdown();
    }
}

如下就表示成功

③ 单向发送

java 复制代码
/**
 * 单向发送
 * @author: lyd
 * @date: 2025/12/30 22:57
 */
public class OnewayProducer {
    public static final int MESSAGE_COUNT = 10;
    public static final String PRODUCER_GROUP = "Test03";
    public static final String DEFAULT_NAMESRVADDR = "192.168.109.134:9876";
    public static final String TOPIC = "Topic-Lyd";
    public static final String TAG = "Lyd";
    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, MQBrokerException, RemotingException, InterruptedException {
        // 使用生产者组名进行实例化
        DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);
        producer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
        producer.start();
        for (int i = 0; i < MESSAGE_COUNT; i++) {
            Message msg = new Message(TOPIC,
                    TAG,
                    ("单发消息:Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
            );
            // 调用发送消息以将消息传递到代理之一。
            producer.sendOneway(msg);
            System.out.printf(i + "-发送成功");
        }
        Thread.sleep(5000);
        /*
         * 当生产者实例不再使用时关闭。
         */
        producer.shutdown();
    }
}

运行结果如下

4.2 消费者

消息的生产者分为两种:

  • 拉模式:消费者主动到 broker 上去拉取。
  • 推模式:消费者被动等待 broker 推送过来。

① 拉模式

如下代码,我们通过订阅"Topic-Lyd"主题,循环进行拉取,通过获取注册的 topic,在进行便利主题的消息队列,通过计算偏移量来主动获取消息,最后打印读取的消息并且将偏移量后移。

java 复制代码
public class PullConsumer {

    public static void main(String[] args) throws MQClientException {
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("Test01");
        consumer.setNamesrvAddr("192.168.109.134:9876");
        Set<String> topics = new HashSet<>();
        topics.add("Topic-Lyd");
        consumer.setRegisterTopics(topics);
        consumer.start();
        // 循环拉取
        while (true) {
            consumer.getRegisterTopics().forEach(t -> {
                try {
                    // 获取主题中的队列
                    Set<MessageQueue> messageQueues = consumer.fetchMessageQueuesInBalance(t);
                    messageQueues.forEach(query -> {
                        try {
                            // 获取query的偏移量
                            long offset = consumer.getOffsetStore().readOffset(query, ReadOffsetType.READ_FROM_MEMORY);
                            if (offset < 0) {
                                offset = consumer.getOffsetStore().readOffset(query, ReadOffsetType.READ_FROM_STORE);
                            }
                            if (offset < 0) {
                                offset = consumer.maxOffset(query);
                            }
                            if (offset < 0) {
                                offset = 0;
                            }
                            // 拉取query的消息,每次11条
                            PullResult pullResult = consumer.pull(query, "*", offset, 11);
//                            System.out.printf("循环拉取消息ing %s%n", pullResult);
                            switch (pullResult.getPullStatus()) {
                                case FOUND:
                                    pullResult.getMsgFoundList().forEach(p -> {
                                        System.out.println("消息拉取成功:" + p);
                                    });
                                    // 更新偏移量
                                    consumer.updateConsumeOffset(query, pullResult.getNextBeginOffset());
                            }
                        } catch (MQClientException e) {
                            throw new RuntimeException(e);
                        } catch (MQBrokerException e) {
                            throw new RuntimeException(e);
                        } catch (RemotingException e) {
                            throw new RuntimeException(e);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    });
                } catch (MQClientException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}

当我启动一个生产者推送数据的时候,这里就能看到拉取成功

② 推模式

以下例子是通过设置消息监听器:MessageListenerConcurrently 进行获取得到消息并进行消费。

java 复制代码
public class PushConsumer {
    public static void main(String[] args) throws MQClientException {
        // 创建默认的推模式消费者实例,消费者组名为"Test01"
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("Test01");
        
        // 设置NameServer地址,用于连接RocketMQ集群
        consumer.setNamesrvAddr("192.168.109.134:9876");
        
        // 订阅主题"Topic-Lyd","*"表示订阅所有标签的消息
        consumer.subscribe("Topic-Lyd", "*");
        
        // 设置消息监听器,处理接收到的消息
        consumer.setMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
            // 遍历消息列表并打印每条消息
            list.forEach(m -> {
                System.out.println("收到消息:" + m);
            });
            // 返回消费成功状态
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        
        // 启动消费者
        consumer.start();
        System.out.println("消费者已启动!");
    }
}

5 总结

RocketMQ 就是一个高性能的消息中转站,让系统之间解耦、异步、可靠地通信。安装时注意改内存,使用时先启动 NameServer 和 Broker,然后按需选择收发方式即可。本文介绍了什么 RocketMQ 以及单机安装和简单的案例,对于集群部署,因为这篇文章是我弄完之后的产出,集群部署要截图的比较多,后续在重新整理。


转发请携带作者信息 @怒放吧德德 @一个有梦有戏的人

持续创作很不容易,作者将以尽可能的详细把所学知识分享各位开发者,一起进步一起学习。转载请携带链接,转载到微信公众号请勿选择原创,谢谢!

👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍

谢谢支持!

相关推荐
篱笆院的狗15 小时前
Java 中的 DelayQueue 和 ScheduledThreadPool 有什么区别?
java·开发语言
2501_9418091415 小时前
面向多活架构与数据地域隔离的互联网系统设计思考与多语言工程实现实践分享记录
java·开发语言·python
qualifying16 小时前
JavaEE——多线程(4)
java·开发语言·java-ee
better_liang16 小时前
每日Java面试场景题知识点之-DDD领域驱动设计
java·ddd·实体·领域驱动设计·架构设计·聚合根·企业级开发
li.wz16 小时前
Spring Bean 生命周期解析
java·后端·spring
sanggou16 小时前
【实战总结】Spring Boot 后端接口防抖详解与实现方案(含注解 + Redis)
spring boot·后端
czlczl2002092517 小时前
深入解析 ThreadLocal:架构演进、内存泄漏与数据一致性分析
java·jvm·架构
Victor35617 小时前
Hibernate(26)什么是Hibernate的透明持久化?
后端
盖世英雄酱5813617 小时前
不是所有的this调用会导致事务失效
java·后端
Victor35617 小时前
Hibernate(25)Hibernate的批量操作是什么?
后端