NameServer启动流程
NameServer 启动类是 org.apache.rocketmq.namesrv.NamesrvStartup,下面我们从源码角度分析 NameServer 的启动流程,重点关注 NameServer 相关启动参数。
第一步:首先来解析配置文件,需要填充 NamesrvConfig、NettyServerConfig 属性值,如代码清单2-1 所示。
final NamesrvConfig namesrvConfig = new NamesrvConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
nettyServerConfig.setListenPort(9876);
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, " + file + "%n");
in.close();
}
}
if (commandLine.hasOption('p')) {
MixAll.printObjectProperties(null, namesrvConfig);
MixAll.printObjectProperties(null, nettyServerConfig);
System.exit(0);
}
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);
代码清单2-1 中,先创建 NamesrvConfig(NameServer 业务参数)、NettyServerConfig(NameServer 网络参数),然后在解析启动时把指定的配置文件或启动命令中的选项值填充到 NamesrvConfig、NettyServerConfig 对象中。参数来源有如下两种方式,NamesrvConfig 属性如代码清单2-2 所示,NettyServerConfig 属性如代码清单2-3 所示。
-
-c configFile 通过 -c 命令指定配置文件的路径。
-
使用 “--属性名 属性值” 命令,例如 --listenPort 9876。
private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY,
System.getenv(MixAll.ROCKETMQ_HOME_ENV));
private String kvConfigPath = System.getProperty("user.home") + File.separator
+ "namesrv" + File.separator + "kvConfig.json";
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;
-
rocketmqhome:RocketMQ 主目录,通过 -Drocketmq.home.dir=path 或设置环境变量 ROCKETMQ_HOME 可以配置 RocketMQ 的主目录。
-
kvConfigPath:NameServer 存储 KV 配置属性的持久化路径。
-
configStorePath:NameServer 默认配置文件路径。NameServer 启动时如果要通过配置文件配置 NameServer 启动属性,请使用
-c
选项。 -
orderMessageEnable:是否支持顺序消息,默认是不支持。
private int listenPort = 8888;
private int serverWorkerThreads = 8;
private int serverCallbackExecutorThreads = 0;
private int serverSelectorThreads = 3;
private int serverOnewaySemaphoreValue = 256;
private int serverAsyncSemaphoreValue = 64;
private int serverChannelMaxIdleTimeSeconds = 120;
private int serverSocketSndBufSize = NettySystemConfig.socketSndbufSize;
private int serverSocketRcvBufSize = NettySystemConfig.socketRcvbufSize;
private boolean serverPooledByteBufAllocatorEnable = true;
private boolean useEpollNativeSelector = false;
-
listenPort:NameServer 监听端口,该值默认会被初始化为 9876。
-
serverWorkerThreads:Netty 业务线程池线程个数。
-
serverCallbackExecutorThreads:Netty public 任务线程池线程个数。Netty 网络会根据业务类型创建不同的线程池,比如处理消息发送、消息消费、心跳检测等。如果该业务类型(RequestCode)未注册线程池,则由 public 线程池执行。
-
serverSelectorThreads:I/O 线程池线程个数,主要是 NameServer、Broker 端解析请求、返回相应的线程个数。这类线程主要用于处理网络请求,先解析请求包,然后转发到各个业务线程池完成具体的业务操作,最后将结果返回给调用方。
-
serverOnewaySemaphoreValue:send oneway 消息请求的并发度(Broker 端参数)。
-
serverAsyncSemaphoreValue:异步消息发送的最大并发度(Broker 端参数)。
-
serverChannelMaxIdleTimeSeconds:网络连接最大空闲时间,默认为 120s。如果连接空闲时间超过该参数设置的值,连接将被关闭。
-
serverSocketSndBufSize:网络 socket 发送缓存区大小,默认为 64KB。
-
serverSocketRcvBufSize:网络 socket 接收缓存区大小,默认为 64KB。
-
serverPooledByteBufAllocatorEnable:ByteBuffer 是否开启缓存,建议开启。
-
useEpollNativeSelector:是否启用 Epoll I/O 模型,Linux 环境下建议开启。
在启动 NameServer 时,可以先使用 |
第二步:根据启动属性创建 NamesrvController 实例并初始化,NameServerController 实例为 NameServer 核心控制器,如代码清单2-4 所示。
public boolean initialize() {
this.kvConfigManager.load();
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
this.remotingExecutor = Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(),
new ThreadFactoryImpl("RemotingExecutorThread_"));
this.registerProcessor();
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
NamesrvController.this.kvConfigManager.printAllPeriodically();
}
}, 1, 10, TimeUnit.MINUTES);
return true;
}
加载 KV 配置,先创建 NettyServer 网络处理对象,然后开启两个定时任务,在 RocketMQ 中此类定时任务统称为心跳检测。
-
定时任务1:NameServer 每隔 10s 扫描一次 Broker,移除处于未激活状态的 Broker。
-
定时任务2:NameServer 每隔 10min 打印一次 KV 配置。
第三步:注册 JVM 钩子函数并启动服务器,以便监听 Broker、消息生产者的网络请求,如代码清单2-5 所示。
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
public Void call() throws Exception {
controller.shutdown();
return null;
}
}));
controller.start();
这里主要是向读者展示一种常用的编程技巧,如果代码中使用了线程池,一种优雅停机的方式就是注册一个 JVM 钩子函数,在 JVM 进程关闭之前,先将线程池关闭,及时释放资源。