Nacos 作为配置中心,在配置文件被更新之后,客户端如何知道服务端已经有内容变动了呢?
这篇文章重点分析和学习这个机制。
初始化NcosManager
Nacos 客户端在启动的时候,首先初始化 NacosConfigManager 类。
@Bean
public NacosConfigManager nacosConfigManager(
NacosConfigProperties nacosConfigProperties) {
return new NacosConfigManager(nacosConfigProperties);
}
创建ConfigService
NacosConfigManager 初始化的过程中,接着创建 ConfigService 类。
public static ConfigService createConfigService(Properties properties) {
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
return vendorImpl;
}
创建 ClientWorker
ConfigService 通过构造函数创建对象的时候,接着创建了 ClientWorker 对象。到现在为止,我们才发现 ClientWorker 是真正干活的对象。
ClientWorker 对初始化自己的时候,启动了一个异步线程,默认是每隔五秒钟向 Nacos 服务器拉取一次配置信息。
public void startInternal() {
executor.schedule(() -> {
while (!executor.isShutdown() && !executor.isTerminated()) {
try {
listenExecutebell.poll(5L, TimeUnit.SECONDS);
if (executor.isShutdown() || executor.isTerminated()) {
continue;
}
executeConfigListen();
} catch (Exception e) {
LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e);
}
}
}, 0L, TimeUnit.MILLISECONDS);
}
这是一个无限循环的任务,只要线程池不中断或者退出,会一直请求下去。具体是如何做到无线循环的,可以在接下来的步骤中看到。
向服务器发请求,拉取配置信息
RpcClient#request,客户端在3秒时间内,重试3次向服务器拉取最新的配置信息。
while (retryTimes < RETRY_TIMES && System.currentTimeMillis()
< timeoutMills + start) {
//
}
如果成功拉取到配置信息,开始循环遍历,服务端哪些 Key 的属性值发生了变化。
刷新内容和检查
如果刚才有 key 属性值发生了变化,拿着这些变化的 Key 向服务端发送请求,拉取最新的内容,超时时间为3秒。ClientWorker#refreshContentAndCheck。
ConfigResponse response = getServerConfig(cacheData.dataId, cacheData.group, cacheData.tenant, 3000L,notify);
cacheData.setContent(response.getContent());
cacheData.setEncryptedDataKey(response.getEncryptedDataKey());
if (null != response.getConfigType()) {
cacheData.setType(response.getConfigType());
}
cacheData.checkListenerMd5();
注册Nacos 监听事件
接下来又开启了一个异步线程,为当前客户端注册 NacosContextRefresher#registerNacosListener 监听器。
这个监听器负责发布服务端键值对更新的事件。
private void registerNacosListener(final String groupKey, final String dataKey) {
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
}
最终,负责监听这个事件的 RefreshEventListener 在接收到事件变化之后,将发生变化的 Key 打印出来,通知客户端 Nacos 服务端配置信息发生了变化。
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
log.debug("Event received " + event.getEventDesc());
Set<String> keys = this.refresh.refresh();
log.info("Refresh keys changed: " + keys);
}
}
总结,Nacos 服务端配置信息发生了变更之后,不是由服务端推送给客户端的。正确的理解是这个样子,客户端定时向服务端循环发送请求,拉取服务端配置信息,如果有变更,就自动更新自己的值为最新值。
Nacos 为什么要设置成客户端拉取服务端配置信息呢?因为 Nacos 客户端和服务端维持的是短链接,有超时时间,时间过了之后,链接会自动断开,无法获取服务端信息。
如果使用长链接的话,连接的客户端很多,会对客户端造成一定压力。