如果让我们自己实现一个分布式配置中心,从那些方面考虑呢?
服务器端的配置保存(持久化)?
服务器端提供什么样的访问api?
数据变化之后如何通知到客户端?push(服务端主动推送到客户端)、pull(客户端主动拉去数据)? -> 长轮训( pull数据量很大会怎么办)
客户端如何去获得远程服务的数据?
安全性
刷盘(本地缓存)
那么我们看看 Nacos 是否按照我们的设想实现的呢?
- Nacos客户端从服务端获取数据的example
- 上述的代码首先创建一个congfigService ,是通过反射的方式创建一个 NacosConfigService
- 通过getConfig 获取服务器上配置的内容
- 通过http 的方式向服务端获取远端服务的配置内容
- 服务端通过 SpringMVC的方式提供一个Get 方法供客户端获取数据使用
至此整个初次获取服务端配置信息的流程就完成了,那么大家考虑一个问题,如果服务端的数据发生变化时,客户端是是如何感知的?是通过定时的pull 还是服务端自动推送呢(push)?
一、客户端如何将可能发生变化的数据告诉给服务端?
刚刚看上面的代码,客户端通过反射的方式创建了一个NacosConfigService ,那么我们是否可以通过 NacosConfigService 创建来入手呢?
那么我们看看是否应对上述的话
- 接下来核心内容就要到了
public NacosConfigService(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties);
String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
if (StringUtils.isBlank(encodeTmp)) {
encode = Constants.ENCODE;
} else {
encode = encodeTmp.trim();
}
initNamespace(properties);
agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
agent.start();
worker = new ClientWorker(agent, configFilterChainManager, properties); //核心方法,我没看看是否会这里面实现 上述的 pull 或者push 呢?
}
- 该类的定义如下: 初始化2个线程池的定义, 跟一个定时执行的线程池
public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager, final Properties properties) {
this.agent = agent;
this.configFilterChainManager = configFilterChainManager;
// Initialize the timeout parameter
init(properties);
executor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
t.setDaemon(true);
return t;
}
});
executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
t.setDaemon(true);
return t;
}
});
executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
checkConfigInfo(); //定时的检查配置是否发生变化
} catch (Throwable e) {
LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
}
}
}, 1L, 10L, TimeUnit.MILLISECONDS);
}
-
分批去检查配置内容是否发生变化
-
长轮询任务执行内容:
-
检查本地缓存的内容是否发生变化
-通过事件监听通知本地缓存(本地cache 跟本地文件 不一致,用本地文件的内容作为最新内容,通知给客户端)通过checkListenerMd5()方法我们可以看出发送一个通知,代码如下:
-
本地文件拿不到最新内容,就要从远端服务器获取最新内容。
-请求服务端获取发生变化的组装过程 -
真正的请求
二、 服务端拿到请求后如何处理?
1. 长轮询检查 客户端认为会发生变化的groupKey
2.判断兼容性质
3.如果检查到可能变化的groupKey 在服务器端确实发生变化,则将变化的groupKey 反回给客户端。
4.如果没有检查出变化,则30s-0.5s 后再检查一次,看是否发生变化,这次检查完不管有没有发生变化,都进行返回。
5.通过开启一个定时任务的线程去进行再次检查,并将检查后的结果进行返回。