NameServer主要提供路由管理、服务注册、及服务发现的机制。
一、NameServer架构设计
消息中间件的设计思路一般基于主题的订阅发布机制。消息生产者发送某一主题的消息到消息服务器,消息服务器负责该消息的持久化存储,消息消费者订阅感兴趣的主题,消息服务器根据订阅信息【路由信息】将消息推送到消费者【PUSH模式】或者消息消费者主动向消息服务器拉取消息【PULL消息】从而实现消息生产者与消费者的解耦。
此时会出现以下问题:
- 如何避免消息服务器的单点故障?
- 消息生产者如何如何知道消息要发往哪台消息服务器?
- 如果某一台消息服务器宕机了,生产者如何在不重启服务器的情况下感知呢?
对于问题 1 我们通常会通过部署多台消息服务器来共同承担消息服务器的存储。NameServer本身的高可用可通过部署多台NameServer服务器来实现,但彼此之间互不通信,也就是某一刻NameServer服务器之间的数据并不完全相同,但这对消息发送不会造成任何影响。
对于问题 2 Broker消息服务器在启动时向所有NameServer注册,消息生产者在发送消息之前先从NameServer获取Broker服务器地址列表,然后根据负载算法从列表中选择一台消息服务器进行消息发送。
对于问题 3 NameServer与每台Broker服务器保持长连接,并间隔10s检测Broker是否存活,如果检测到Broker宕机,则从路由注册表中将其移除。但是路由变化不会马上通知消息生产者【为了降低NameServer实现复杂度,在消息发送端提供容错机制保证消息发送高可用】
二、NameServer启动流程
NamesrvStartup中的main方法调用如下main0方法:
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;
}
我们可以发现是需要创建NameServerController ,而创建NameServerController需要初始化
final NamesrvConfig namesrvConfig = new NamesrvConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
创建NameServerController后需要调用ininialize方法
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);
}
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
@Override
public Void call() throws Exception {
controller.shutdown();
return null;
}
}));
controller.start();
return controller;
}
然后注册JVM钩子函数启动服务器,以便监听Broker,