本文章主要参考《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架构设计与实现原理