前言
前面的文章中介绍了RocketMQ一些用法和特性(优点,集群等),接下来的文章咱们一起了解下RocketMQ的源码。NameServer 是RocketMQ的核心组件之一,咱们先拿NameServer“开刀吧”。这篇文章主要围绕 NamesrvStartup
和 NamesrvController
这两个核心类(启动类)来展开,在这里先感谢各位小伙伴儿们的支持啦!!!
NameServer 概述
在前面的文章中,我们已经介绍过NameServer的作用了,为了让大家了解的更全面,在这里再给NameServer一个特写!!!NameServer 是 RocketMQ 的服务发现组件,它主要负责:
- Broker 管理:Broker 启动时会向 NameServer 注册(重点),NameServer 会维护 Broker 的数据信息。
- 路由信息管理:Producer 和 Consumer 通过 NameServer 获取 Broker 的路由信息,以此进行消息的发送和消费。
NameServer 启动流程
为什么在前言部分说主要介绍两个核心类呢 》》》因为,NameServer 的启动过程主要分为两个阶段:
- 启动类
NamesrvStartup
:负责解析命令行参数、初始化配置、启动NamesrvController
。 - 核心控制器
NamesrvController
:负责管理 NameServer 的核心逻辑,包括路由信息的管理、Broker 的注册与注销等。
详细的源码我们下面聊!
NamesrvStartup 启动类
NamesrvStartup
是 NameServer 的启动入口类,它主要负责解析命令行参数、初始化配置、启动 NamesrvController
。
启动流程
从头开始看是调用了main0这个方法,接着又调用了createNamesrvController这个方法。
源码和每一块主要做了什么:
public static NamesrvController main0(String[] args) {
try {
// 解析命令行参数
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
System.exit(-1);
return null;
}
// 初始化日志配置
final Logger log = LoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
String namesrvConfigFile = commandLine.getOptionValue('c');
String rocketmqHome = commandLine.getOptionValue('h');
if (UtilAll.isBlank(namesrvConfigFile)) {
namesrvConfigFile = System.getProperty("rocketmq.namesrv.config.file", "namesrv.properties");
}
if (UtilAll.isBlank(rocketmqHome)) {
rocketmqHome = System.getenv("ROCKETMQ_HOME");
}
// 加载配置文件
NamesrvConfig namesrvConfig = new NamesrvConfig();
NettyServerConfig nettyServerConfig = new NettyServerConfig();
nettyServerConfig.setListenPort(9876);
if (namesrvConfigFile != null) {
InputStream in = new BufferedInputStream(new FileInputStream(namesrvConfigFile));
properties = new Properties();
properties.load(in);
MixAll.properties2Object(properties, namesrvConfig);
MixAll.properties2Object(properties, nettyServerConfig);
namesrvConfig.setConfigStorePath(namesrvConfigFile);
in.close();
}
// 设置环境变量
MixAll.printObjectProperties(log, namesrvConfig);
MixAll.printObjectProperties(log, nettyServerConfig);
final NamesrvController controller = createNamesrvController(namesrvConfig, nettyServerConfig);
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;
}
这都是一些配置环境,非核心代码。
看到这里,我们可以了解到NamesrvConfig是NameServer的配置参数,NettyServerConfig是接收网络请求的配置参数,底层是使用Nettey实现的,并且设置了监听端口为9876。
所以上面代码归根结底就是:
- 解析命令行参数:
NamesrvStartup
解析命令行参数,获取配置文件路径这些必要信息。 - 初始化配置:根据命令行参数,加载,解析配置文件,初始化
NamesrvConfig
和NettyServerConfig
。 - 创建并启动
NamesrvController
:通过createNamesrvController
方法创建NamesrvController
实例,调用start
方法启动。
NamesrvController核心控制器
NamesrvController
是 NameServer 的核心控制器,负责管理 NameServer 的核心逻辑,包括路由信息的管理、Broker 的注册和注销等等。
初始化流程
public boolean initialize() {
// 加载 KV 配置
this.kvConfigManager.load();
// 初始化 Netty 服务器
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
// 注册处理器
this.registerProcessor();
// 启动定时任务
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
return true;
}
所以,NamesrvController
的初始化流程主要包括以下几个步骤:
- 初始化 KV 配置管理器:
KVConfigManager
负责管理 NameServer 的 KV 配置。 - 初始化路由信息管理器:
RouteInfoManager
负责管理 Broker 的路由信息。 - 初始化 Netty 服务器:
RemotingServer
负责处理与 Broker、Producer、Consumer 的网络通信。 - 启动定时任务:定时扫描不活跃的 Broker,清理路由信息。
/**
* 定期扫描并移除不活跃的Broker连接。
* 这个方法会遍历当前存储的所有Broker连接,并检查每个连接是否已经超时。
* 便利,如果当前Broker连接的最后更新时间超过了设定的过期时间,关闭这个连接并从表中移除。
*/
public void scanNotActiveBroker() {
// 获取还存活的Broker的迭代器
Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
while (it.hasNext()) {
Entry<String, BrokerLiveInfo> next = it.next();
// 获取便利的这个Broker连接的最后更新时间戳
long last = next.getValue().getLastUpdateTimestamp();
// 检查这个Broker连接是否已经超过设定的过期时间
if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
// 如果超过过期时间,关闭这个Broker的网络通道
RemotingUtil.closeChannel(next.getValue().getChannel());
// 从brokerLiveTable中移除这个Broker的记录
it.remove();
log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
}
}
}
RouteInfoManager路由信息管理器
RouteInfoManager
也是 NameServer 的核心组件之一,它负责管理 Broker 的路由信息。它维护了以下这几个关键数据结构:
- HashMapString topic , ListQueueData topicQueueTable:存储每个 Topic 的队列信息。
- HashMapString brokerName , BrokerData brokerAddrTable:存储每个 Broker 的地址信息。
- HashMapString clusterName , SetString brokerName clusterAddrTable:存储每个集群的 Broker 信息。
- HashMapString brokerAddr , BrokerLiveInfo brokerLiveTable:存储每个 Broker 的活跃状态信息,这个上面提到过。
RemotingServer网络通信
RemotingServer
是 NameServer 的网络通信组件,底层是基于 Netty 实现(看名字也能看得出来hhhh)。它负责处理与 Broker、Producer、Consumer 的网络通信。
public class NettyRemotingServer extends NettyRemotingAbstract implements RemotingServer {
private final ServerBootstrap serverBootstrap;
private final EventLoopGroup eventLoopGroupSelector;
private final EventLoopGroup eventLoopGroupBoss;
}
定时任务
NameServer 会定期扫描不活跃的 Broker,清理它的路由信息。定时任务通过 ScheduledExecutorService
实现,默认每隔 10 秒执行一次。这个咱们上面详细介绍过了!!!
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
NameServer 的底层启动过程
通过我们上面的介绍,这里再总结一下,RocketMQ的NameServer是如何启动并运行的:
- 命令行参数解析:
NamesrvStartup
首先解析命令行参数,并加载配置文件。- 创建
NamesrvController
实例:初始化NamesrvController
,加载K-V配置。- 注册处理器:在
NamesrvController
中注册各种处理器,以便处理不同类型的请求。- 启动Netty服务器:启动Netty服务器,开始监听来自Broker和客户端的请求。
- 定时任务:定期扫描不活跃的Broker和打印K-V配置信息,确保系统的健康运行。
以上过程结束,除了加载配置文件辣么多配置很难理解外,整体流程还是比较清晰的,做了以上流程图,可以配合理解和复习使用。
文章小结
本篇文章到这里就结束了,后续会继续分享RocketMQ相关的知识,感谢各位小伙伴们的支持!