RocketMQ源码分析(第二篇)-NameServer启动流程

643 阅读3分钟

本文章主要参考《RocketMQ技术内幕:RocketMQ架构设计与实现原理》一书,主要是将自己学习的过程进行记录和梳理,方便以后翻阅。

1. NameServer的作用

上面是一张RocketMQ的物理部署图,其中,NameServer类似zookeeper,为整个RocketMQ集群提供协调和治理,主要维护Borker、Topic的信息,监控Broker的运行状态。

Broker在启动的时候,会将自己相关的信息注册到所有的NameServer中。并且,NameServer会不断检测Broker的状态,将宕机的Broker从注册列表中移除。

Producer在发送消息的时候,先从NameServer中获取Broker服务器的信息,然后根据负载算法选择某一个Broker发送消息。

Consumer会定期从NameServer中获取topic的路由信息。

2. NameServer的启动过程

在部署RocketMQ环境的时候,都会先将NameServer服务启动,NameServer的启动类为:org.apache.rocketmq.namesrv.NamesrvStartup。

#org.apache.rocketmq.namesrv.NamesrvStartup

public static NamesrvController main0(String[] args) {

    try {
        //创建启动控制类
        NamesrvController controller = createNamesrvController(args);
        //启动
        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的启动主要包括如下几步:

  • 解析启动参数
  • 初始化启动控制类
  • 启动一个扫描Broker并将宕机的Broker移除的定时任务
  • 启动一个打印KV配置信息的定时任务
  • 注册钩子函数
  • 启动RemotingServer

2.1 解析启动参数

#org.apache.rocketmq.namesrv.NamesrvStartup#createNamesrvController

Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
    System.exit(-1);
    return null;
}

启动参数主要有四个: -h : 帮助说明 -n : 指定NameServer的地址 -c : 指定NameServer配置文件 -p : 打印所有的参数

#org.apache.rocketmq.namesrv.NamesrvStartup#createNamesrvController

final NamesrvConfig namesrvConfig = new NamesrvConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
nettyServerConfig.setListenPort(9876);
if (commandLine.hasOption('c')) {//如果存在-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();
    }
}

if (commandLine.hasOption('p')) {//如果有-p打印参数,打印所有的配置信息,并退出
    InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
    MixAll.printObjectProperties(console, namesrvConfig);
    MixAll.printObjectProperties(console, nettyServerConfig);
    System.exit(0);
}

//获取通过 --属性名 属性值 格式的配置信息
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);

首先创建两个启动配置文件,NamesrvConfig和NettyServerConfig,如果存在-c命令指定了配置文件,将配置文件中的配置赋值到两个配置文件中,并且将命令行中形如(--属性名 属性)的配置也加载到NamesrvConfig中。

2.2 初始化启动控制类

#org.apache.rocketmq.namesrv.NamesrvStartup#createNamesrvController

final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

首先,根据配置文件创建一个NamesrvController,NamesrvController是NameServer的启动控制类,负责NameServer的启动。

#org.apache.rocketmq.namesrv.NamesrvController#initialize

/**
 * 加载kv配置信息,加载/namesrv/KvConfig.json中的kv 
this.kvConfigManager.load();

/**
 * 创建nettyServer网络处理对象
 */
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);

/**
 * 创建NettyServer执行的线程池
 */
this.remotingExecutor = Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(),
    new ThreadFactoryImpl("RemotingExecutorThread_"));

this.registerProcessor();

初始化的时候,主要是加载KV配置信息,并初始化负责网络处理的RemotingServer和相关的线程池,然后注册负责处理所有NameServer的请求的处理器。

2.3 启动定时任务

#org.apache.rocketmq.namesrv.NamesrvController#initialize

/**
 * 创建一个定时任务,每隔10秒扫描一次broker,移除处于不激活状态的broker
 */
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable(){

    @Override
    public void run() {

        //todo 如何实现
        NamesrvController.this.routeInfoManager.scanNotActiveBroker();
    }
}, 5, 10, TimeUnit.SECONDS);

/**
 * 创建一个定时任务,每隔十分钟打印一次KV配置信息
 */
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable(){

    @Override
    public void run() {

        NamesrvController.this.kvConfigManager.printAllPeriodically();
    }
}, 1, 10, TimeUnit.MINUTES);

2.4 注册钩子函数

#org.apache.rocketmq.namesrv.NamesrvStartup#start

//添加钩子函数
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>(){

    @Override
    public Void call() throws Exception {

        controller.shutdown();
        return null;
    }
}));

2.4 启动RemotingServer

#org.apache.rocketmq.namesrv.NamesrvController#start

this.remotingServer.start();

if (this.fileWatchService != null) {
    this.fileWatchService.start();
}

3. 参考资料

  • RocketMQ技术内幕:RocketMQ架构设计与实现原理