文章目录
- 源码目录
- 模块入口代码的功能 `NamesrvStartup`
- 入口函数:
- 解析命令行参数
- 初始化NameServer 的Controller
- NameServer 的总控逻辑
- 初始化执行线程池
- 启动通信服务
- 核心业务逻辑处理
- 集群状态存储
- 具体结构
- 控制访问这些结构的锁机制
- 事件监听 `BrokerHousekeepingService`
- 参考
源码目录
- 整个功能很简单,一共就 8 个类
- KVConfigManager
- KVConfigSerializeWrapper
- ClusterTestRequestProcessor
- DefaultRequestProcessor
- BrokerHousekeepingService
- RouteInfoManager.java
- NamesrvController
- NamesrvStartup
- 依赖核心模块:
rocketmq-remoting
模块入口代码的功能 NamesrvStartup
入口函数:
-
NamesrvStartup
是NameServer
模块的启动入口,NamesrvController
是用来协块各个调模功能的代码 -
NamesrvStartup.java
类的入口函数main
,发现把逻辑转到main0()
函数 -
main0()
函数的主要功能:
- 是解析命令行参数,重点是解析
-c 和-p
参数 - 初始化
NameServer
的Controller
,即初始化了NamesrvController.java
类
解析命令行参数
- 解析命令行参数源码
# 解析命令行参数
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
System.exit(-1);
return null;
}
# 判断参数 -c
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, %s%n", file);
in.close();
}
}
# 判断 -p 后退出
if (commandLine.hasOption('p')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
MixAll.printObjectProperties(console, namesrvConfig);
MixAll.printObjectProperties(console, nettyServerConfig);
System.exit(0);
}
- 简单分析
-
-c
命令行参数用来指定配置文件的位置 -
-p
命令行参数用来打印所有配置项的值 - 注意,用
-p
参数打印配置项的值之后程序就退出了,这是一个帮助调试的选项
初始化NameServer 的Controller
-
main0
函数的另外一个功能是初始化NamesrvController
,源码如下
- 根据解析出的配置参数, 调用
controller.initialize()
来初始化,然后调用controller.start()
让NameServer
开始服务 - 还有一个逻辑是注册
ShutdownHookThread
,当程序退出的时候会调用controller.shutdown
来做退出前的清理工作
# createNamesrvController() 方法中,在解析参数后面
# 解析出配置参数
final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);
// remember all configs to prevent discard
controller.getConfiguration().registerConfig(properties);
# start(controller) 方法中
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);
}
# 注册 `ShutdownHookThread`, 当程序退出的时候会调用`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;
}
NameServer 的总控逻辑
NameServer
的总控逻辑在NamesrvController.java
代码中。NameServer
是集群的协调者,它只是简单地接收其他角色报上来的状态,然后根据请求返回相应的状态
初始化执行线程池
-
NameserverController.initialize()
函数完成,源码如下: - 一共3个线程池
- 启动了一个默认是8 个线程的线程池
- 1个定时线程池,负责扫描失效的Broker(scanNotActiveBroker)
- 1个定时线程池,打印配置信息(printAllPeriodically)
# 启动了一个默认是8 个线程的线程池
this.remotingExecutor =
Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
# 定时线程池,负责扫描失效的Broker(scanNotActiveBroker)
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
# 定时线程池,打印配置信息(printAllPeriodically)
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.kvConfigManager.printAllPeriodically();
}
}, 1, 10, TimeUnit.MINUTES);
启动通信服务
- 启动负责通信的服务
remotingServer
-
remotingServer
监昕一些端口,收到Broker
、Client
等发过来的请求后,根据请求的命令,调用不同的Processor
来处理。这些不同的处理逻辑被放到上面初始化的线程池中执行 -
remotingServer
是基于Netty 封装的一个网络通信服务
# NameserverController.initialize() 方法中
# 初始化 remotingServer
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
# 注册 Processor
this.registerProcessor();
private void registerProcessor() {
if (namesrvConfig.isClusterTest()) {
this.remotingServer.registerDefaultProcessor(new ClusterTestRequestProcessor(this, namesrvConfig.getProductEnvName()),
this.remotingExecutor);
} else {
this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.remotingExecutor);
}
}
核心业务逻辑处理
- NameServer 的核心业务逻辑, 在
DefaultRequestProcessor.java
的processRequest
方法中 中,网络通信服务模块收到请求后,就调用这个Processor
来处理
2.逻辑主体是个switch
语句,根据RequestCode
调用不同的函数来处理,从RequestCode
可以了解到Name Server
的主要功能,比如:
-
REGISTER_BROKER
是在集群中新加入一个Broker 机器 -
GET_ROUTEINTO_BY_TOPIC
是请求获取一个Topic 的路由信息 -
WIPE_WRITE_PERM_OF_BROKER
是删除一个Broker 的写权限
switch (request.getCode()) {
case RequestCode.PUT_KV_CONFIG:
return this.putKVConfig(ctx, request);
case RequestCode.GET_KV_CONFIG:
return this.getKVConfig(ctx, request);
case RequestCode.DELETE_KV_CONFIG:
return this.deleteKVConfig(ctx, request);
case RequestCode.QUERY_DATA_VERSION:
return queryBrokerTopicConfig(ctx, request);
case RequestCode.REGISTER_BROKER:
Version brokerVersion = MQVersion.value2Version(request.getVersion());
if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
return this.registerBrokerWithFilterServer(ctx, request);
} else {
return this.registerBroker(ctx, request);
}
case RequestCode.UNREGISTER_BROKER:
return this.unregisterBroker(ctx, request);
case RequestCode.GET_ROUTEINTO_BY_TOPIC:
return this.getRouteInfoByTopic(ctx, request);
case RequestCode.GET_BROKER_CLUSTER_INFO:
return this.getBrokerClusterInfo(ctx, request);
case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
return this.wipeWritePermOfBroker(ctx, request);
case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:
return getAllTopicListFromNameserver(ctx, request);
case RequestCode.DELETE_TOPIC_IN_NAMESRV:
return deleteTopicInNamesrv(ctx, request);
case RequestCode.GET_KVLIST_BY_NAMESPACE:
return this.getKVListByNamespace(ctx, request);
case RequestCode.GET_TOPICS_BY_CLUSTER:
return this.getTopicsByCluster(ctx, request);
case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS:
return this.getSystemTopicListFromNs(ctx, request);
case RequestCode.GET_UNIT_TOPIC_LIST:
return this.getUnitTopicList(ctx, request);
case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST:
return this.getHasUnitSubTopicList(ctx, request);
case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST:
return this.getHasUnitSubUnUnitTopicList(ctx, request);
case RequestCode.UPDATE_NAMESRV_CONFIG:
return this.updateConfig(ctx, request);
case RequestCode.GET_NAMESRV_CONFIG:
return this.getConfig(ctx, request);
default:
break;
}
集群状态存储
具体结构
-
NameServer
作为集群的协调者,需要保存和维护集群的各种元数据, 这是通过RoutelnfoManager
类来实现的 - 每个结构存储着一类集群信息
属性 | 描述 |
HashMap<String/* topic */, List> topicQueueTable | topicQueueTable 这个结构的Key 是Topic 的名称,它存储了所有Topic的属性信息。Value 是个QueueData 队列, 队里的长度等于这个Topic数据存储的Master Broker 的个数, QueueData 里存储着Broker 的名称、读写queue 的数量、同步标识等 |
HashMap<String/* brokerName */, BrokerData> brokerAddrTable; | BrokerAddrTable以BrokerName 为索引,相同名称的Broker 可能存在多台机器, 一个Master 和多个Slave 。这个结构存储着一个BrokerName 对应的属性信息,包括所属的Cluster 名称, 一个Master Broker 和多个Slave Broker的地址信息 |
HashMap<String/* clusterName /, Set<String/ brokerName */>> clusterAddrTable; | ClusterAddrTable存储的是集群中C luster 的信息,结果很简单,就是一个Cluster 名称对应一个由BrokerName 组成的集合 |
HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable; | 这个结构和BrokerAddrTable 有关系,但是内容完全不同,这个结构的Key 是BrokerAddr ,也就是对应着一台机器, BrokerAddrTable 中的Key是BrokerName , 多个机器的BrokerName 可以相同。BrokerLiveTable存储的内容是这台Broker 机器的实时状态,包括上次更新状态的时间戳, NameServer 会定期检查这个时间戳,超时没有更新就认为这个Broker 无效了,将其从Broker 列表里清除 |
HashMap<String/* brokerAddr /, List/ Filter Server */> filterServerTable; | Filter Server 是过滤服务器,是RocketMQ 的一种服务端过滤方式,一个Broker 可以有一个或多个F ilter Server 。这个结构的Key 是Broker的地址, Value 是和这个Broker 关联的多个Filter Server 的地址 |
- 源码如下
private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
public RouteInfoManager() {
this.topicQueueTable = new HashMap<String, List<QueueData>>(1024);
this.brokerAddrTable = new HashMap<String, BrokerData>(128);
this.clusterAddrTable = new HashMap<String, Set<String>>(32);
this.brokerLiveTable = new HashMap<String, BrokerLiveInfo>(256);
this.filterServerTable = new HashMap<String, List<String>>(256);
}
控制访问这些结构的锁机制
- 在
NameServer
的场景中,读取操作多,更改操作少(读多写少),所以选择读写锁能大大提高效率
private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
RoutelnfoManager
中使用的是可重人的读写锁,以deleteTopic
函数为例:
public void deleteTopic(final String topic) {
try {
try {
this.lock.writeLock().lockInterruptibly();
this.topicQueueTable.remove(topic);
} finally {
this.lock.writeLock().unlock();
}
} catch (Exception e) {
log.error("deleteTopic Exception", e);
}
}
事件监听 BrokerHousekeepingService
- 继承
ChannelEventListener
类,主要负责连接断开的回调与监听 - 监听的事件如:
onChannelConnect
、onChannelClose
、onChannelException
、onChannelIdle
- 在监听的回调方法中,会调用
NamesrvController
中RouteInfoManager
中的方法,更新状态信息等
@Override
public void onChannelClose(String remoteAddr, Channel channel) {
this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
}
@Override
public void onChannelException(String remoteAddr, Channel channel) {
this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
}
@Override
public void onChannelIdle(String remoteAddr, Channel channel) {
this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
}
参考