RocketMQ的元数据管理模块NameServer启动流程分析

NameServer是RocketMQ的元数据管理模块,它负责整个RocketMQ集群的Broker和元数据管理。下面以NameServer的启动流程来说明NameServer的工作原理。

一、启动入口

NamesrvStartup 是的NameServer服务的启动类。 其入口是 main0( ) 方法。代码如下:

public static NamesrvController main0(String[] args) {

        try {
            //创建NameServer的控制器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;
    }

从上面的代码可以看出,NameServer的启动主要是通过启动NamesrvController来完成。

二、NamesrvController的组成

NamesrvController的模块组成如下

public class NamesrvController {
    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);

    //NameSrv的配置信息类
    private final NamesrvConfig namesrvConfig;

    //NameSrv底层的核心服务器 - Netty配置类
    private final NettyServerConfig nettyServerConfig;

    //调度线程池, 执行定时任务, 两件事 : 1.检查存活的broker状态 2.打印配置
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "NSScheduledThread"));
    //管理 NameServer的 kv配置
    private final KVConfigManager kvConfigManager;
    //管理 路由信息的对象
    private final RouteInfoManager routeInfoManager;
    //Netty网络层封装对象 
    private RemotingServer remotingServer;

    // ChannelEventListener接口类   用于监听channel 状态,
    // 当channel状态发生改变时 例如: close idle ... 等状态时 ,该service会监听并处理
    private BrokerHousekeepingService brokerHousekeepingService;

    //业务线程池,netty线程主要任务 是 解析报文, 将报文解析成 RemotingCommand对象, 然后就将该对象交给业务线程池继续处理
    private ExecutorService remotingExecutor;

    // 总配置 (其中包含 NameSrv配置 和  NettyServer配置)
    private Configuration configuration;
    private FileWatchService fileWatchService;

    //NamesrvController构造函数,对各种对象进行赋值
    public NamesrvController(NamesrvConfig namesrvConfig, NettyServerConfig nettyServerConfig) {
        this.namesrvConfig = namesrvConfig;
        this.nettyServerConfig = nettyServerConfig;
        this.kvConfigManager = new KVConfigManager(this);
        this.routeInfoManager = new RouteInfoManager();
        this.brokerHousekeepingService = new BrokerHousekeepingService(this);
        this.configuration = new Configuration(
            log,
            this.namesrvConfig, this.nettyServerConfig
        );
        this.configuration.setStorePathFromConfig(this.namesrvConfig, "configStorePath");
    }
}

以上便是NamesrvController的主要模块,NamesrvController启动时主要通过各种配置参数来启动的,配置参数通过NamesrvConfig来设置

三、NamesrvConfig

public class NamesrvConfig {
    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);

    //从环境变量中 获取 ROCKETMQ_HOME 值
    private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
    //kvConfig.json  路径  kv的存储文件路径
    private String kvConfigPath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "kvConfig.json";
    //namesrv.properties 配置文件路径
    private String configStorePath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "namesrv.properties";
    private String productEnvName = "center";
    private boolean clusterTest = false;
    //顺序消息功能 是否开启  默认关闭
    private boolean orderMessageEnable = false;
}

四、创建namesrv控制器NamesrvController

创建namesrv控制器NamesrvController的流程如下:

    /**
     * 创建 namesrv 控制器
     * @param args  启动参数
     */
    public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
        
        System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));

        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  nameSrv配置
        final NamesrvConfig namesrvConfig = new NamesrvConfig();

        // netty 服务器配置
        final NettyServerConfig nettyServerConfig = new NettyServerConfig();


        // namesrv 服务器 监听端口 修改为 9876
        nettyServerConfig.setListenPort(9876);


        if (commandLine.hasOption('c')) {
            //  读取 -c 选项值
            String file = commandLine.getOptionValue('c');
            if (file != null) {

                // 读取 config 文件数据 到 properties 内
                InputStream in = new BufferedInputStream(new FileInputStream(file));
                properties = new Properties();
                properties.load(in);

                // 如果 config 配置文件 内的配置 涉及到 namesrvConfig 或者 nettyServerConfig 字段进行复写
                MixAll.properties2Object(properties, namesrvConfig);
                MixAll.properties2Object(properties, nettyServerConfig);

                // 将读取的 配置文件 路径 保存到 字段
                namesrvConfig.setConfigStorePath(file);

                System.out.printf("load config properties file OK, %s%n", file);
                in.close();
            }
        }

        if (commandLine.hasOption('p')) {
            InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
            MixAll.printObjectProperties(console, namesrvConfig);
            MixAll.printObjectProperties(console, nettyServerConfig);
            System.exit(0);
        }

        // 将启动时 命令行 设置的 kv 复写到 namesrvConfig 内
        MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);

        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);


        // 创建 控制器
        // 参数1: namesrvConfig
        // 参数2: 网络层配置 nettyServerConfig
        final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

        // remember all configs to prevent discard
        controller.getConfiguration().registerConfig(properties);

        return controller;
    }

从上面的代码可以看出, 创建NamesrvController的流程主要是读取系统环境变量 和 启动参数 来设置 NameSrvConfig 和 NettyServeConfig 配置类, 并通过这俩类构造出 NamesrvController对象。

五、启动控制器NamesrvController

下面来看一下NamesrvController的启动流程

 public static NamesrvController start(final NamesrvController controller) throws Exception {

        if (null == controller) {
            throw new IllegalArgumentException("NamesrvController is null");
        }

        // 初始化方法...
        boolean initResult = controller.initialize();
        if (!initResult) {
            controller.shutdown();
            System.exit(-3);
        }

        // 注册 JVM级别的 Hook, 平滑关机的逻辑。 当JVM被关闭时,主动调用 controller.shutdown()方法平滑关机
        Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                controller.shutdown();
                return null;
            }
        }));

        // 服务器网络层 启动
        controller.start();

        return controller;
    }

上述代码主要 做了 三件事:

  1. 初始化
  2. 注册 JVM Hook 回调平滑关机。
  3. 启动

重点需要关注 第 1点 初始化 , 而第3点的逻辑实际上就是 NettyServer的启动入口了。

六、初始化控制器NamesrvController

public boolean initialize() {

        //加载kv配置
        this.kvConfigManager.load();

        //创建网络服务器对象
        this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);

        //创建业务线程池  默认线程数8
        this.remotingExecutor =
            Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));

        //注册协议处理器 (缺省协议处理器)
        this.registerProcessor();
        //定时任务1: 每10s中检查 broker 存活状态, 将idle状态的broker移除
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.routeInfoManager.scanNotActiveBroker();
            }
        }, 5, 10, TimeUnit.SECONDS);

        //定时任务2: 每10min打印配置信息
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                NamesrvController.this.kvConfigManager.printAllPeriodically();
            }
        }, 1, 10, TimeUnit.MINUTES);

        if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
            // Register a listener to reload SslContext
            try {
                fileWatchService = new FileWatchService(
                    new String[] {
                        TlsSystemConfig.tlsServerCertPath,
                        TlsSystemConfig.tlsServerKeyPath,
                        TlsSystemConfig.tlsServerTrustCertPath
                    },
                    new FileWatchService.Listener() {
                        boolean certChanged, keyChanged = false;
                        @Override
                        public void onChanged(String path) {
                            if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
                                log.info("The trust certificate changed, reload the ssl context");
                                reloadServerSslContext();
                            }
                            if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
                                certChanged = true;
                            }
                            if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {
                                keyChanged = true;
                            }
                            if (certChanged && keyChanged) {
                                log.info("The certificate and private key changed, reload the ssl context");
                                certChanged = keyChanged = false;
                                reloadServerSslContext();
                            }
                        }
                        private void reloadServerSslContext() {
                            ((NettyRemotingServer) remotingServer).loadSslContext();
                        }
                    });
            } catch (Exception e) {
                log.warn("FileWatchService created error, can't load the certificate dynamically");
            }
        }

        return true;
    }

通过上面的代码可以看出,代码的主要流程如下:

  1. 加载NameServer配置信息到 KV配置管理中心 中
  2. 创建NettyServer 对象
  3. 创建 Netty的 业务线程池 remotingExecutor
  4. 将 缺省协议处理器 DefaultRequestProcessor 注册到NettyServer对象中
  5. 分别开启 两个 定时任务:
  6. 检查 broker 存活状态
  7. 打印 kv配置信息
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值