RocketMq之nameserver源码阅读

379 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

下载、编译源码

1.下载源码

点击rocketmq源码,fork一份到自己的repository中

image-20210531101230249.png

通过git工具下载到本地,不擅长命令行的小伙伴,可通过sourcetree等git可视化工具操作;

没有创建自己账号的小伙伴可以直接clone一份代码下来,如下图所示的第一、二步骤所示:

image-20210531101811074.png

实在是不会用git的小伙伴,也可以直接下载项目压缩包,也就是上图所示的第三步。

2.导入项目到idea

image-20210531102327981.png

通过idea导入,选择maven工程

image-20210531102419884.png 下图是导入后的项目结构:

image-20210531102856115.png

至此,项目下载并且导入的工作已经完成。

3.编译项目

找到Terminal。找不到的小伙伴可以点击上面的View,找到Tool Windows中的Terminal

image-20210531103656861.png

在Terminal输入以下命令:

mvn -Prelease-all -DskipTests -Dcheckstyle.skip=true clean install -U

编译完成后,项目中多出一个target文件夹,并且Terminal现实全部编译成功:

image-20210531105715228.png

4.开始运行Nameserver启动代码

image-20210531110252402.png

此时,idea会输出一系列错误,这些都属于代码编译错误。

image-20210531110515610.png

因为我们已经通过maven编译好了代码,所以不需要在每次Run的时候通过idea自动编译,那么就需要修改一下配置

image-20210531110656861.png

选择Run之前需要执行的操作,点击删除。意味着咱们在Run之前啥也不做,直接Run。

image-20210531110916654.png

image-20210531111541679.png

至此,咱们的基本配置全部完成,下一步可以开始通过Debug的方式阅读NameServer的源码了。

Debug调试阅读源码

image-20210531113704120.png

找到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);
        }

image-20210531121059984.png

基于上述源码的阅读,我们想要启动成功,需要在启动参数中设置rocketmq.home.dir

-Drocketmq.home.dir=当前项目所在的路径

image-20210531120744301.png

再次启动main函数。结果报错说找不到日志配置文件

image-20210531121313928.png

// 需要找到日志配置文件
        configurator.doConfigure(namesrvConfig.getRocketmqHome() + "/conf/logback_namesrv.xml");

源码中会在rocketmqHome后面拼接一个/conf/logback_namesrv.xml路径,那么我们就需要根据代码中的要求去创建一个conf文件,并且在里面创建一个logback_namesrv.xml日志配置文件

image-20210531121655914.png

通过翻阅项目模块,我们发现在distribution模块中,已经有一个logback_namesrv.xml日志配置文件了,那么我们直接把它拷贝到新创建的conf文件夹中。

再次运行main入口函数,我们就可以启动成功了。

image-20210531121905562.png

以上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;
    }