一.概要
nameserver是一个简单的topic路由注册中心,类似dubbo中的zookeeper,或者springcloud中的nacos,主要功能如下:
- broker管理,nameserver接受broker集群注册信息,并保存下来,提供心跳机制,检查brocker是否活着
- 保存路由信息,让producer和consumer进行消息的投递和消费
二.启动流程
2.1 main主方法的步骤
public class NamesrvStartup {
public static void main(String[] args) {
main0(args);
controllerManagerMain();
}
2.2 main0(args)方法
public static void main0(String[] args) {
try {
//解析一些命令参数,不重要,省略
parseCommandlineAndConfigFile(args);
// 创建一个controller,并启动服务
createAndStartNamesrvController();
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
}
可以看到,主要是两个步骤:解析命令参数和创建并启动服务
2.2.1 createAndStartNamesrvController方法
public static void createAndStartNamesrvController() throws Exception {
// 创建一个controller并复制某些属性
NamesrvController controller = createNamesrvController();
// 启动nameserver(netty...)
start(controller);
String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();
log.info(tip);
System.out.printf("%s Name Server 启动成功 %n", tip);
}
进入createNamesrvController()方法
public static NamesrvController createNamesrvController() {
// 创建一个controller
final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig, nettyClientConfig);
// remember all configs to prevent discard
// 将当前 properties 合并到项目的配置中,并且当前 properties 会覆盖项目中的配置
controller.getConfiguration().registerConfig(properties);
return controller;
}
这个nameserController创建成功后,接着会start()方法
public static NamesrvController start(final NamesrvController controller) throws Exception {
if (null == controller) {
throw new IllegalArgumentException("NamesrvController is null");
}
/** 初始化,核心做下面两件事
* 1.处理netty相关:创建远程服务与工作线程
* 2.开启定时任务[5s一次]:移除不活跃的broker
*/
boolean initResult = controller.initialize();
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
/**
* 关闭钩子,可以在关闭前进行一些操作
* 在 kill pid的时候会被触发(rocketmq的启动脚本中用的就是kill -pid)
* kill -9 pid 不会被触发
*/
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, (Callable<Void>) () -> {
controller.shutdown();
return null;
}));
// 启动
controller.start();
return controller;
}
上面代码是nameserverController的启动的核心,主要做的事情如下
- 调用
initialize()方法 - 注册
钩子事件,在jvm关闭的时候,会触发钩子事件 - 调用
start()方法,接下来我们一个个来看
A.调用initialize()方法
public boolean initialize() {
// 加载 kv 配置
loadConfig();
// 创建 netty 远程服务,创建 NettyRemotingServer 和 NettyRemotingClient
initiateNetworkComponents();
//初始化一些队列和线程池
initiateThreadExecutors();
// 注册,
// 1.就是把[上一步创建的defaultExecutor] defaultExecutor 注册到 remotingServer作为默认的处理,
// 2.还会注册 GET_ROUTEINFO_BY_TOPIC
registerProcessor();
// 开启定时任务,每隔5s扫描一次broker,移除不活跃的broker
startScheduleService();
// Tls安全传输,我们不关注
initiateSslContext();
initiateRpcHooks();
return true;
}
代码中,会启动netty服务,创建 NettyRemotingServer 和 NettyRemotingClient, 初始化一些队列和线程池,机型注册,还会开启一些定时任务,清除不活跃的broker,这一步是最核心的,接下来来一次来看一下核心代码逻辑
1)创建netty服务
private void initiateNetworkComponents() {
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
this.remotingClient = new NettyRemotingClient(this.nettyClientConfig);
}
我们以 NettyRemotingServer的创建为例,看代码
public NettyRemotingServer(final NettyServerConfig nettyServerConfig,
final ChannelEventListener channelEventListener) {
super(nettyServerConfig.getServerOnewaySemaphoreValue(), nettyServerConfig.getServerAsyncSemaphoreValue());
this.serverBootstrap = new ServerBootstrap();
this.nettyServerConfig = nettyServerConfig;
this.channelEventListener = channelEventListener;
// 创建了一个名为publicExecutor线程池
this.publicExecutor = buildPublicExecutor(nettyServerConfig);
//创建boosGroup
this.eventLoopGroupBoss = buildBossEventLoopGroup();
// 创建workGroup
this.eventLoopGroupSelector = buildEventLoopGroupSelector();
loadSslContext();
}
serverBootstrap:创建netty服务端的启动类publicExecutor:这里创建了一个名为publicExecutor线程池,暂时并不知道这个线程有啥作用,先混个脸熟吧eventLoopGroupBoss与eventLoopGroupSelector线程组:熟悉netty的小伙伴应该对这两个线程很熟悉了,这就是netty用来处理连接事件与读写事件的线程了,eventLoopGroupBoss对应的是netty的boss线程组,eventLoopGroupSelector对应的是worker线程组
到这里,netty服务的准备工作本完成了。
2)初始化一些队列和线程池
private void initiateThreadExecutors() {
this.defaultThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getDefaultThreadPoolQueueCapacity());
this.defaultExecutor = new ThreadPoolExecutor(this.namesrvConfig.getDefaultThreadPoolNums(), this.namesrvConfig.getDefaultThreadPoolNums(), 1000 * 60, TimeUnit.MILLISECONDS, this.defaultThreadPoolQueue, new ThreadFactoryImpl("RemotingExecutorThread_")) {
@Override
protected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {
return new FutureTaskExt<>(runnable, value);
}
};
this.clientRequestThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getClientRequestThreadPoolQueueCapacity());
this.clientRequestExecutor = new ThreadPoolExecutor(this.namesrvConfig.getClientRequestThreadPoolNums(), this.namesrvConfig.getClientRequestThreadPoolNums(), 1000 * 60, TimeUnit.MILLISECONDS, this.clientRequestThreadPoolQueue, new ThreadFactoryImpl("ClientRequestExecutorThread_")) {
@Override
protected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {
return new FutureTaskExt<>(runnable, value);
}
};
}
3)netty注册一些默认的处理器
这些处理器,后面会详细讲解
private void registerProcessor() {
if (namesrvConfig.isClusterTest()) {
this.remotingServer.registerDefaultProcessor(new ClusterTestRequestProcessor(this, namesrvConfig.getProductEnvName()), this.defaultExecutor);
} else {
// Support get route info only temporarily
ClientRequestProcessor clientRequestProcessor = new ClientRequestProcessor(this);
this.remotingServer.registerProcessor(RequestCode.GET_ROUTEINFO_BY_TOPIC, clientRequestProcessor, this.clientRequestExecutor);
// 注册操作
this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.defaultExecutor);
}
}
4) 开启定时任务,清除不活跃的broker
private void startScheduleService() {
/**
* 移除不活跃的broker ,默认每5秒执行一次
* scanNotActiveBroker逻辑: 取map中broker信息, 最后一次更新时间+更新间隔 < 当前时间,就close这个channel
*/
this.scanExecutorService.scheduleAtFixedRate(NamesrvController.this.routeInfoManager::scanNotActiveBroker,
5, this.namesrvConfig.getScanNotActiveBrokerInterval(), TimeUnit.MILLISECONDS);
this.scheduledExecutorService.scheduleAtFixedRate(NamesrvController.this.kvConfigManager::printAllPeriodically,
1, 10, TimeUnit.MINUTES);
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
NamesrvController.this.printWaterMark();
} catch (Throwable e) {
LOGGER.error("printWaterMark error.", e);
}
}, 10, 1, TimeUnit.SECONDS);
}
B.调用 start()方法
public void start() throws Exception {
// 启动nettyServer
this.remotingServer.start();
}
将上一步创建的netty服务,进行启动,进入start()方法
2.2 controllerManagerMain()方法
public void start() {
this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
nettyServerConfig.getServerWorkerThreads(),
new ThreadFactory() {
private final AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NettyServerCodecThread_" + this.threadIndex.incrementAndGet());
}
});
// 初始化 netty的四个handler,后面会添加到netty中的pipeline中
prepareSharableHandlers();
serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
.channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.SO_KEEPALIVE, false)
.childOption(ChannelOption.TCP_NODELAY, true)
//TODO 绑定ip和端口
.localAddress(new InetSocketAddress(this.nettyServerConfig.getBindAddress(),
this.nettyServerConfig.getListenPort()))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline()
//handshakeHandler 处理握手操作,用来判断tls的开启状态
.addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler)
/**
* 批量添加五个handler
* encoder/NettyDecoder:处理报文的编解码操作
* IdleStateHandler:处理心跳
* connectionManageHandler:处理连接请求
* serverHandler:处理读写请求=> TODO 用来处理broker注册消息、producer/consumer获取topic消息的
*/
.addLast(defaultEventExecutorGroup,
encoder,
new NettyDecoder(),
new IdleStateHandler(0, 0,
nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
connectionManageHandler,
//serverHandler:处理读写请求
serverHandler
);
}
});
addCustomConfig(serverBootstrap);
try {
ChannelFuture sync = serverBootstrap.bind().sync();
InetSocketAddress addr = (InetSocketAddress) sync.channel().localAddress();
if (0 == nettyServerConfig.getListenPort()) {
this.nettyServerConfig.setListenPort(addr.getPort());
}
log.info("RemotingServer started, listening {}:{}", this.nettyServerConfig.getBindAddress(),
this.nettyServerConfig.getListenPort());
this.remotingServerTable.put(this.nettyServerConfig.getListenPort(), this);
} catch (Exception e) {
throw new IllegalStateException(String.format("Failed to bind to %s:%d", nettyServerConfig.getBindAddress(),
nettyServerConfig.getListenPort()), e);
}
if (this.channelEventListener != null) {
this.nettyEventExecutor.start();
}
this.timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
NettyRemotingServer.this.scanResponseTable();
} catch (Throwable e) {
log.error("scanResponseTable exception", e);
}
}
}, 1000 * 3, 1000);
}
上面主要就是对netty绑定ip和端口, 注册四个handler,分别是编解码/心跳/处理连接请求/处理读写请求
三.总结
本文主要分析了NameServer的启动流程,整个启动流程分为3步:
-
创建
controller:这一步主要是解析nameServer的配置并完成赋值操作 -
初始化
controller:主要创建了NettyRemotingServer对象、netty服务线程池、定时任务 -
启动
controller:就是启动netty服务