之前比较概览的介绍了nacos配置中心的使用:https://www.imooc.com/article/306481
这一次针对配置中心监听配置变更进行进一步的学习。
nacos如何实现配置更新
1-首先我们需要了解,nacos分为客户端和服务端。我们使用客户端的代码依赖来进行
发布配置和监听配置的更新。而配置信息会保存到nacos的服务端。
2-使用客户端的代码去监听配置的变更
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("receiveConfigInfo:"+configInfo);
}
});
3-NacosConfigService会进行初始化,并且每隔10ms去定期校验配置,方法为
checkConfigInfo()
checkConfigInfo方法中会对所有配置进行分批,默认每3000个配置属于同一批次,
并且一个批次的配置拥有相同的taskId。然后将每个批次交由另一个线程池去进行检
查。
executorService.execute(new LongPollingRunnable(i));
4-每个LongPollingRunnable任务中会对相同taskId的一批次配置进行检查。
5-在nacos中通过CacheData来表示一个配置信息。上面所说的taskId是属于
CacheData中的一个属性。CacheDat还包含具体的配置内容,md5值等信息。并且通过
dataId, group可以获取到对应的CacheData。
6-每个CacheData都维护了一个listeners集合
CopyOnWriteArrayList<ManagerListenerWrap> listeners;
并且提供了add和remove方法:
当使用客户端代码进行监听配置的时候,会进行addListener,
cache.addListener(listener);
listeners.addIfAbsent(wrap);
会将其添加到当前CacheData的listeners集合中。
当配置信息发生变更,则会调用safeNotifyListener方法去回调客户端添加的
listener的方法:listener.receiveConfigInfo(contentTmp);让客户端获取到最
新的配置。
7-LongPollingRunnable中的run方法中的checkUpdateDataIds方法会返回当前批次
发生变更的配置信息。
到这里,客户端需要通过http请求和服务端进行交互,获取变更的配置信息。
8-checkUpdateConfigStr方法向服务端发送http请求,设置了超时时间
headers.add("Long-Pulling-Timeout");
headers.add("" + timeout);
HttpResult result = agent.httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener",
headers, params, agent.getEncode(), readTimeoutMs);
9-服务端通过ConfigController类(@PostMapping("/listener"))接口来处理客
户端的http请求。
这里触发了一个长轮询的方法
inner.doPollingConfig(request, response, clientMd5Map,
probeModify.length());
longPollingService.addLongPollingClient(request, response,
clientMd5Map, probeRequestSize);
10-先来看看LongPollingService类,继承自AbstractEventListener,
在初始化的时候构造函数中会进行如下操作:
public AbstractEventListener() {
EventDispatcher.addEventListener(this);
}
也就是LongPollingService会将自己加入到EventDispatcher中的集合中,如下:
CopyOnWriteArrayList<AbstractEventListener> listeners;
11-LongPollingService会将http请求提交给线程池去处理,每个任务封装在
ClientLongPolling中。ClientLongPolling首先会去比较当前请求批次的配置
是否有变更,通过md5值比较:
List<String> changedGroups = MD5Util.compareMd5(
(HttpServletRequest)asyncContext.getRequest(),
(HttpServletResponse)asyncContext.getResponse(), clientMd5Map)
如果当前批次有变更的配置,则会返回当前的http请求。否则则会等待,直到超时(30s)
12-如果超时之前有配置发生了变更,会怎么办?
首先nacos服务端会收到客户端关于配置变更的请求,由CommunicationController
类的notifyConfigInfo接口去处理:
dumpService.dump(dataId, group, tenant, lastModifiedTs, handleIp,
true);
dumpTaskMgr.addTask(groupKey,new DumpTask(groupKey, lastModified,
handleIp, isBeta));
dumpTaskMgr 初始化的时候会启动一个线程去处理每个配置的DumpTask任务,并且
最终会调用
ConfigService的updateMd5(groupKey, md5, lastModifiedTs); 方法,
if (cache.md5 == null || !cache.md5.equals(md5)) {
cache.md5 = md5;
cache.lastModifiedTs = lastModifiedTs;
EventDispatcher.fireEvent(newLocalDataChangeEvent(groupKey))
}
这里触发了 EventDispatcher.fireEvent方法,会去遍历所有添加的listener
for (AbstractEventListener listener : getEntry(event.getClass()).listeners) {
try {
listener.onEvent(event);
} catch (Exception e) {
log.error(e.toString(), e);
}
}
最终会调用LongPollingService的onEvent方法,
public void onEvent(Event event) {
if (isFixedPolling()) {
} else {
if (event instanceof LocalDataChangeEvent) {
LocalDataChangeEvent evt = (LocalDataChangeEvent)event;
scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
}
}
}
13-DataChangeTask任务会将变更的配置信息通过groupKey获取到其所属的
ClientLongPolling任务,并且进行http的返回:
clientSub.sendResponse(Arrays.asList(groupKey));
14-最终http请的结果返回到客户端,LongPollingRunnable中,会进行
cacheData.checkListenerMd5();
调用CacheData的safeNotifyListener(dataId, group, content, type,
md5, wrap);方法。将变更的配置返回给客户端。
最后的最后,LongPollingRunnable又将自己提交到线程池进行下次的执行:
executorService.execute(this);
总结
打开App,阅读手记