偷师第二步——搭建源码实验室与消息的“诞生”

引言:从“拥有地图”到“打造工具”

在前两篇文章中我们确立了此行的目标与“读码心法”,然后绘制了一幅清晰的RocketMQ宏观架构“藏宝图”,认识了四大核心角色。可以说,我们现在“心中有数,眼中有图”。

然而,河水到底多深,还是要自己下脚去试试才能有更深刻的感受。在真正深入那片蕴藏着无尽宝藏的源码森林之前,我们还缺一件最关键的东西——一套属于我们自己的、精良的“探索工具”

这套工具,就是我们今天要亲手搭建的。一个可以随时单步调试、任意修改、反复运行的本地源码环境,对于我们理解一个复杂系统而言,其价值是任何文档都无法替代的。它能让我们:

  • 将抽象的流程具象化:当理论让困惑时,逐行的调试,亲眼看看代码是如何执行的,所有疑惑都将烟消云散。

  • 无畏地进行实验:想知道如果某个参数改了会怎样?想验证一段逻辑在异常情况下如何处理?在本地实验室里,可以尽情“破坏”,而无需担心任何线上风险。

  • 成为系统的主人:当能随心所欲地驾驭和观察它的每一个细节时,它在眼中就不再是一个神秘的黑盒,而是完全掌控的“白盒”。

因此,本文将是一次纯粹的、手把手的动手实践。我们将分为两个部分:

  1. 第一部分:搭建“源码实验室”。我们将从零开始,一步步地克隆、编译、配置并成功在IDE中运行一个本地的RocketMQ集群。

  2. 第二部分:消息的“诞生”。我们将编写第一个Producer,成功发送一条消息,通过Debug,亲眼见证这条消息“诞生”的瞬间,找到其真正起点。

一、 搭建“源码实验室”

1. 准备环境

在开始之前,请确保已经安装了以下工具:

  1. Git:用于从代码仓库克隆源码。

  2. JDK:RocketMQ基于Java开发,最好确保的JDK版本不低于1.8。

  3. Maven 3.5+:用于项目的构建和依赖管理。

  4. IntelliJ IDEA:强烈推荐使用IDEA,其强大的Debug和代码分析功能,是我们此行的“瑞士军刀”。(当然,Eclipse用户也可以参照类似步骤进行)。

2. 第一步:克隆源码与本地构建

a. 克隆源码:打开的命令行工具,执行以下命令,从Apache的官方GitHub仓库克隆最新的RocketMQ源码。

git clone https://github.com/apache/rocketmq.git

建议: 如果GitHub访问速度较慢,可以使用其中文官方网站(https://rocketmq.apache.org/zh/download)下载压缩包。

b. 切换到稳定版本:为了保证学习的稳定性,我们不直接使用主分支的开发代码,而是切换到一个相对稳定的release版本,例如 release-4.9.0。如果是下载压缩包,那就直接选择这个4.9.0版本下载即可。

cd rocketmq
git checkout release-4.9.0

c. 使用Maven构建:在rocketmq根目录下,执行Maven构建命令。

mvn clean install -DskipTests

-DskipTests参数会跳过单元测试,可以大幅加快构建速度。当在控制台看到[INFO] BUILD SUCCESS时,恭喜,RocketMQ的“零件”已经被我们成功生产出来了。

3. 第二步:在IDEA中配置并启动“指挥中心”—— NameServer

现在,我们要在IDEA中,把这些“零件”组装成一个能运行的系统。首先启动NameServer。

  • 导入项目:在IDEA中,选择File -> Open,找到刚才的rocketmq文件夹,将其作为Maven项目导入。等待IDEA索引完成。

  • 找到启动类:在项目结构中,导航到namesrv模块,找到启动类:org.apache.rocketmq.namesrv.NamesrvStartup.java

  • 配置环境变量(关键步骤):在启动前,RocketMQ需要知道它的“家”在哪里,以便存放日志等文件。我们需要配置一个ROCKETMQ_HOME环境变量。

    • 在IDEA中,点击Run -> Edit Configurations...

    • 点击左上角的+号,选择Application

    • Name: NameServer

    • Main class: 选择我们刚才找到的NamesrvStartup

    • Environment variables: 点击旁边的按钮,添加一个环境变量,Name: ROCKETMQ_HOMEValue: 可以指定一个电脑上的目录,例如/Users/yourname/rocketmq_home

    • 点击OK保存。

  • 启动:现在,可以直接运行刚才创建的NameServer配置。当在控制台看到类似The Name Server boot success...的日志时,我们的“调度指挥中心”就已经开始工作了!

4. 第三步:启动“心脏中枢”—— Broker

NameServer启动后,它只是一个空壳的指挥部,没有任何“士兵”向它报到。现在,我们来启动核心的Broker。

  • 找到启动类:导航到broker模块,找到启动类:org.apache.rocketmq.broker.BrokerStartup

  • 创建配置文件(关键步骤):Broker的配置比NameServer复杂,我们需要一个配置文件来告诉它NameServer的地址、自己是谁等信息。

    • rocketmq源码的distribution/conf目录下,会找到很多配置文件模板。我们选择broker.conf

    • 复制一份broker.conf到刚才创建的ROCKETMQ_HOME目录下(例如/Users/yourname/rocketmq_home/conf/broker.conf)。

    • 修改这份新的broker.conf文件,确保以下配置是正确的:

# broker名字,master节点必须是broker-a
brokerName = broker-a
# Master Broker的ID,0表示是Master
brokerId = 0
# NameServer地址,指向我们本地启动的NameServer
namesrvAddr = 127.0.0.1:9876
# CommitLog存储路径
storePathRootDir = /Users/yourname/rocketmq_home/store
# ConsumeQueue存储路径
storePathConsumeQueue = /Users/yourname/rocketmq_home/store/consumequeue
# 消息索引存储路径
storePathIndex = /Users/yourname/rocketmq_home/store/index
# checkpoint文件路径
storeCheckpoint = /Users/yourname/rocketmq_home/store/checkpoint
# abort文件路径
abortFile = /Users/yourname/rocketmq_home/store/abort

请务必将路径替换为自己电脑的实际路径。

  • 配置并启动Broker

    • 和NameServer一样,创建一个新的Application运行配置。

    • Name: Broker

    • Main class: BrokerStartup

    • Program arguments: -c /Users/yourname/rocketmq_home/conf/broker.conf (这里的-c参数,就是告诉Broker去哪里加载配置文件)。

    • Environment variables: 同样需要配置ROCKETMQ_HOME

    • 点击OK保存。

  • 启动:运行Broker配置。观察NameServer的控制台,会看到Broker前来“报到”的日志。同时,Broker自己的控制台也会显示The broker[broker-a, x.x.x.x:10911] boot success...

至此,恭喜!一个功能完整的、本地的、随时可以被我们“随便玩”的RocketMQ核心集群,已经成功运行起来了!我们的“源码实验室”搭建完成!

二、 消息的“诞生”

实验室已经就绪,现在,向这个系统发送第一条消息,并通过Debug,找到这条消息生命周期的入口。

1. 编写我们的第一个“发件人”—— Producer

在的项目中,创建一个新的Maven模块(例如rocketmq-example),并引入rocketmq-client依赖。然后,编写以下代码:

package com.architect.mq.demo

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.nio.charset.StandardCharsets;

/**
@author yuda
测试producer发消息
*/
public class MyProducer {
    public static void main(String[] args) throws Exception {
        // 1. 创建一个Producer,指定一个Group Name
        DefaultMQProducer producer = new DefaultMQProducer("MY_PRODUCER_GROUP");

        // 2. 指定NameServer的地址
        producer.setNamesrvAddr("127.0.0.1:9876");

        // 3. 启动Producer
        producer.start();
        System.out.println("Producer Started.");

        // 4. 创建一条消息
        // Topic: 我们消息要发往的topic
        // Body: 消息的内容
        Message msg = new Message("MY_TOPIC",
                                  "Hello RocketMQ!".getBytes(StandardCharsets.UTF_8));

        // 5. 发送消息
        System.out.println("Sending message...");
        SendResult sendResult = producer.send(msg);

        // 6. 打印发送结果
        System.out.printf("Message Sent Successfully: %s%n", sendResult);

        // 7. 关闭Producer
        producer.shutdown();
        System.out.println("Producer Shutdown.");
    }
}
2. 见证奇迹的时刻

直接运行MyProducermain方法。如果一切顺利,将在控制台看到类似的打印内容:

Producer Started.
Sending message...
Message Sent Successfully: SendResult [sendStatus=SEND_OK, msgId=..., offsetMsgId=..., messageQueue=..., queueOffset=...]
Producer Shutdown.

当看到SEND_OK时,请给自己一点掌声!已经完成了与这个复杂分布式系统的第一次成功交互。发送的消息,此刻已经存储在Broker的磁盘上。

3. 找到入口:设置我们的第一个断点

发送成功只是第一步,我们的目标是“偷师”。现在,让我们找到消息“诞生”的真正起点。

  • 进入producer.send(msg)方法,进入到DefaultMQProducer类中的send()方法。

  • 会看到它内部调用了this.defaultMQProducerImpl.send(msg)。继续跟进去,将来到DefaultMQProducerImpl类。

  • 在这个类里,会看到一个核心的发送方法sendDefaultImpl(...)这就是我们要找的入口!

  • 在这个方法的第一行,打上一个断点

现在,以Debug模式,重新运行MyProducer。程序将会在设置的断点处停下来。

通过观察调用栈,可以清晰地看到main方法是如何一步步调用,最终走到这里的。观察变量,可以看到我们创建的Message对象,以及producer实例内部的所有状态。

这个断点,就是我们下一篇文章,深入探索Producer内部世界的“起点”。

结语:从“旁观者”到“参与者”

今天,我们没有深入任何一行复杂的源码,但通过亲手搭建起这个“源码实验室”,我们已经成功地将自己的角色,从一个RocketMQ的“旁观者”,转变为一个可以随时介入、观察、甚至改变其行为的“参与者”。

我们成功地启动了核心集群,发送了第一条消息,并且精准地找到了探索消息发送之旅的入口——sendDefaultImpl方法。

从下一篇文章开始,我们将从这个断点出发,正式踏入消息的内部世界,去探寻它在发送过程中,是如何找到正确的Broker(路由与负载均衡),以及Broker又是如何用何种方式,将我们的消息写入磁盘,创造出惊人的性能奇迹。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值