持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
NameServer注册中心类似kafka的注册中zookeeper。
1、NameServer
NameServer是一个Broker和Topic路由的注册中心,支持Broker的动态注册和发现
主要包括两个功能:
1、Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;
2、路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer和Consumer仍然可以动态感知Broker的路由的信息。
3、NameServer无状态方式有什么优缺点?
优点:NameServer集群搭建简单
缺点:对于Broker,必须明确指出所有NameServer地址,否则未指出的将不会去注册。NameServer不能随便扩容
1.1、NameServer启动流程
public static NamesrvController main0(String[] args) {
try {
//1、 首先来解析配置文件,需要填充NameServerConfig、NettyServerConfig属性值。
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;
}
public static NamesrvController start(final NamesrvController controller) throws Exception {
if (null == controller) {
throw new IllegalArgumentException("NamesrvController is null");
}
// 2、根据启动属性创建NamesrvController实例,并初始化该实例,nameServercontroller实例为NameServer核心控制器。
boolean initResult = controller.initialize();
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
// 3、注册JVM钩子函数并启动服务器,以便监听Broker、消息生产者的网络请求
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
@Override
public Void call() throws Exception {
controller.shutdown();
return null;
}
}));
controller.start();
return controller;
}
1、 创建NameServerConfig、NettyServerConfig实例,然后再解析启动时的参数,读取配置文件,将属性填充到namesevConfig、nettyServerConfig对象中。
public class NamesrvConfig {
// 主目录
private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
// 存储kv配置属性的持久化路径
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;
public class NettyServerConfig implements Cloneable {
// 监听端口,该值会被初始化为9876
private int listenPort = 8888;
// 业务线程池数
private int serverWorkerThreads = 8;
// 回调线程数
private int serverCallbackExecutorThreads = 0;
// IO线程池数
private int serverSelectorThreads = 3;
// send oneway消息请求并发度
private int serverOnewaySemaphoreValue = 256;
// 异步消息发送最大并发度
private int serverAsyncSemaphoreValue = 64;
// 网络连接最大空闲时间,默认120秒
private int serverChannelMaxIdleTimeSeconds = 120;
private int serverSocketSndBufSize = NettySystemConfig.socketSndbufSize;
private int serverSocketRcvBufSize = NettySystemConfig.socketRcvbufSize;
private int writeBufferHighWaterMark = NettySystemConfig.writeBufferHighWaterMark;
private int writeBufferLowWaterMark = NettySystemConfig.writeBufferLowWaterMark;
private int serverSocketBacklog = NettySystemConfig.socketBacklog;
private boolean serverPooledByteBufAllocatorEnable = true;
2、根据启动属性创建NamesrvController实例,并初始化该实例,nameServercontroller实例为NameServer核心控制器。这一步主要看controller.initialize();函数
public boolean initialize() {
// 1、加载kv配置
this.kvConfigManager.load();
//2、创建nettyServer网络处理对象
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
// 3、线程池
this.remotingExecutor =
Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
this.registerProcessor();
// 定时任务1, nameServer每隔10s扫描一次broker,移除处于不激活状态的Broker。
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.routeInfoManager.scanNotActiveBroker();
}
}, 5, 10, TimeUnit.SECONDS);
// 定时任务2,nameServer每隔10分钟打印一次KV配置。
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
NamesrvController.this.kvConfigManager.printAllPeriodically();
}
}, 1, 10, TimeUnit.MINUTES);
if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
// Register a listener to reload SslContext
try {
fileWatchService = new FileWatchService(
new String[] {
TlsSystemConfig.tlsServerCertPath,
TlsSystemConfig.tlsServerKeyPath,
TlsSystemConfig.tlsServerTrustCertPath
},
new FileWatchService.Listener() {
boolean certChanged, keyChanged = false;
@Override
public void onChanged(String path) {
if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
log.info("The trust certificate changed, reload the ssl context");
reloadServerSslContext();
}
if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
certChanged = true;
}
if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {
keyChanged = true;
}
if (certChanged && keyChanged) {
log.info("The certificate and private key changed, reload the ssl context");
certChanged = keyChanged = false;
reloadServerSslContext();
}
}
private void reloadServerSslContext() {
((NettyRemotingServer) remotingServer).loadSslContext();
}
});
} catch (Exception e) {
log.warn("FileWatchService created error, can't load the certificate dynamically");
}
}
return true;
}
3、 注册JVM钩子函数并启动服务器,开始监听Broker、消息生产者的网络请求。
1.2、元数据注册流程源码分析
元数据注册两种场景
1、在Broker节点在启动的时候,轮询NameServer列表, Broker与每个NameServer节点建立长连接,发起注册请求。
2、Broker节点为了证明自己是存活的,会将最新的信息上报给NameServer,然后每隔30秒向 NameServer发送心跳包。