下载、编译源码
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; }