持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
下载、编译源码
1.下载源码
点击rocketmq源码,fork一份到自己的repository中
通过git工具下载到本地,不擅长命令行的小伙伴,可通过sourcetree等git可视化工具操作;
没有创建自己账号的小伙伴可以直接clone一份代码下来,如下图所示的第一、二步骤所示:
实在是不会用git的小伙伴,也可以直接下载项目压缩包,也就是上图所示的第三步。
2.导入项目到idea
通过idea导入,选择maven工程
下图是导入后的项目结构:
至此,项目下载并且导入的工作已经完成。
3.编译项目
找到Terminal。找不到的小伙伴可以点击上面的View,找到Tool Windows中的Terminal
在Terminal输入以下命令:
mvn -Prelease-all -DskipTests -Dcheckstyle.skip=true clean install -U
编译完成后,项目中多出一个target文件夹,并且Terminal现实全部编译成功:
4.开始运行Nameserver启动代码
此时,idea会输出一系列错误,这些都属于代码编译错误。
因为我们已经通过maven编译好了代码,所以不需要在每次Run的时候通过idea自动编译,那么就需要修改一下配置
选择Run之前需要执行的操作,点击删除。意味着咱们在Run之前啥也不做,直接Run。
至此,咱们的基本配置全部完成,下一步可以开始通过Debug的方式阅读NameServer的源码了。
Debug调试阅读源码
找到main入口函数,发现直接调用了main0(String[] args)函数。
main0(String[] args)函数中第一步先创建NamesrvController
public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
// 设置版本号
System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
// 构建命令行选项-h,-c
Options options = ServerUtil.buildCommandlineOptions(new Options());
// 解析命令行选项,生成一个commandLine对象
commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
System.exit(-1);
return null;
}
// 创建一个NamesrvConfig对象,里面的属性默认会查找rocketmq.home.dir参数或ROCKETMQ_HOME环境变量
final NamesrvConfig namesrvConfig = new NamesrvConfig();
// 其实重点就是获取这两个配置对象
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
nettyServerConfig.setListenPort(9876);
// 读取配置文件,并更新namesrvConfig
if (commandLine.hasOption('c')) {
String file = commandLine.getOptionValue('c');
if (file != null) {
InputStream in = new BufferedInputStream(new FileInputStream(file));
properties = new Properties();
properties.load(in);
MixAll.properties2Object(properties, namesrvConfig);
MixAll.properties2Object(properties, nettyServerConfig);
namesrvConfig.setConfigStorePath(file);
System.out.printf("load config properties file OK, %s%n", file);
in.close();
}
}
// 如果启动命令中有-p,那么就打印namesrvConfig和nettyServerConfig后结束程序
if (commandLine.hasOption('p')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
MixAll.printObjectProperties(console, namesrvConfig);
MixAll.printObjectProperties(console, nettyServerConfig);
System.exit(0);
}
// 把读取到的配置文件全部设置到namesrvConfig中
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);
// 如果没有找搭配参数rocketmq.home.dir或者ROCKETMQ_HOME环境变量,那么就直接结束程序
if (null == namesrvConfig.getRocketmqHome()) {
System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation%n", MixAll.ROCKETMQ_HOME_ENV);
System.exit(-2);
}
// 准备日志对象
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.reset();
// 需要找到日志配置文件
configurator.doConfigure(namesrvConfig.getRocketmqHome() + "/conf/logback_namesrv.xml");
log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
// 打印两个配置对象
MixAll.printObjectProperties(log, namesrvConfig);
MixAll.printObjectProperties(log, nettyServerConfig);
//这个才是真正的创建NamesrvController对象,前提是需要准备好namesrvConfig和nettyServerConfig
final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);
// 把所有的配置写回到nameserver的配置文件中
// remember all configs to prevent discard
controller.getConfiguration().registerConfig(properties);
return controller;
}
在上述源码可以看出,如果没有在启动参数中设置rocketmq.home.dir,或者没有配置ROCKETMQ_HOME环境变量,那么程序就会直接启动失败。
// 如果没有找搭配参数rocketmq.home.dir或者ROCKETMQ_HOME环境变量,那么就直接结束程序
if (null == namesrvConfig.getRocketmqHome()) {
System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation%n", MixAll.ROCKETMQ_HOME_ENV);
System.exit(-2);
}
基于上述源码的阅读,我们想要启动成功,需要在启动参数中设置rocketmq.home.dir
-Drocketmq.home.dir=当前项目所在的路径
再次启动main函数。结果报错说找不到日志配置文件
// 需要找到日志配置文件
configurator.doConfigure(namesrvConfig.getRocketmqHome() + "/conf/logback_namesrv.xml");
源码中会在rocketmqHome后面拼接一个/conf/logback_namesrv.xml路径,那么我们就需要根据代码中的要求去创建一个conf文件,并且在里面创建一个logback_namesrv.xml日志配置文件
通过翻阅项目模块,我们发现在distribution模块中,已经有一个logback_namesrv.xml日志配置文件了,那么我们直接把它拷贝到新创建的conf文件夹中。
再次运行main入口函数,我们就可以启动成功了。
以上createNamesrvController(String[] args)函数做的事情,可以总结为两点:
1.根本目的就是为了创建NamesrvController对象
2.创建一个NamesrvController对象需要先创建NamesrvConfig和NettyServerConfig对象
所以这个函数里面大部分都是在处理配置参数。创建好NamesrvController后,就开始启动它了。
public static NamesrvController main0(String[] args) {
try {
//创建NamesrvController
NamesrvController controller = createNamesrvController(args);
//启动NamesrvController
start(controller);
String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
log.info(tip);
System.out.printf("%s%n", tip);
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}