1:nacos交互模型
nacos配置心的交互模型是pull模型,应用长轮询的方式来获取配置数据。
2:长轮询
由服务端控制响应客户端请求的返回时间,减少客户端无效请求。
客户端发起请求后,服务端不会立即返回结果,而是将请求挂起一段时间,如果此段时间内服务端数据变更,则立即响应客户端请求,如果一直无变化则等到超时时间后响应请求,客户端再重新发起长连接。
3:nacos架构简述
4:源码分析
基于nacos 1.4.2 版本分析
先了解一下nacos在客户端的几个对象
//ClientWorker类属性
//key是groupKey,由dataId,group,tenant拼接的字符串
//value是cacheData对象,每个dataId都会持有一个cacheData对象
private final ConcurrentHashMap<String, CacheData> cacheMap = new ConcurrentHashMap();
1:ConfigFactory工厂类
2:NacosConfigService
重点关注其中两个方法 getConfig(…) 和 getConfigAndSignListener(…) ;其中getconfig方法只是发送普通的http请求获取配置信息,而getConfigAndSignListener方法则多了addTenantListenersWithContent(…)方法,此方法主要作用是发起长轮询和对dataId数据变更注册监听的操作;进入方法看一下
方法很简单,我们主要观察addCacheDataIfAbsent(…)做了什么事情,点进去方法
可以看到,先从本地缓存cacheMap中取值CacheData,如果取不到则向服务端发起长轮询请求获取配置,默认超时时间30s,并把返回数据填充到cacheMap中key对应CacheData对象。在addTenantListenersWithContent(…)方法中注册监听器。
接下来看一下CacheData对象
重点关注listeners属性和addListener(…),checkListenerMd5()方法,还有静态内部类ManagerListenerWrap。属性md5是content真实配置数据计算出来的md5值。
public void addListener(Listener listener) {
if (null == listener) {
throw new IllegalArgumentException("listener is null");
} else {
// 添加监听时会把最新的md5值赋值给ManagerListenerWrap的lastCallMd5属性
CacheData.ManagerListenerWrap wrap = listener instanceof AbstractConfigChangeListener ? new CacheData.ManagerListenerWrap(listener, this.md5, this.content) : new CacheData.ManagerListenerWrap(listener, this.md5);
if (this.listeners.addIfAbsent(wrap)) {
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", new Object[]{this.name, this.tenant, this.dataId, this.group, this.listeners.size()});
}
}
}
void checkListenerMd5() {
Iterator var1 = this.listeners.iterator();
while(var1.hasNext()) {
CacheData.ManagerListenerWrap wrap = (CacheData.ManagerListenerWrap)var1.next();
//对比当前md5值和之前md5值是否一致,不一致则执行safeNotifyListener方法异步更新对应配置
if (!this.md5.equals(wrap.lastCallMd5)) {
this.safeNotifyListener(this.dataId, this.group, this.content, this.type, this.md5, this.encryptedDataKey, wrap);
}
}
}
private static class ManagerListenerWrap {
//监听器
final Listener listener;
//lastMD5值
String lastCallMd5 = CacheData.getMd5String((String)null);
//last数据
String lastContent = null;
ManagerListenerWrap(Listener listener) {
this.listener = listener;
}
ManagerListenerWrap(Listener listener, String md5) {
this.listener = listener;
this.lastCallMd5 = md5;
}
ManagerListenerWrap(Listener listener, String md5, String lastContent) {
this.listener = listener;
this.lastCallMd5 = md5;
this.lastContent = lastContent;
}
}
再看NacosConfigService初始化时会初始化属性ClientWorker,ClientWorker在init时加载超时配置,再就是初始化了两个线程池
两个线程初始化之后,executer只有一个线程,用来定时执行checkConfigInfo(…)方法,我么可以看出来是在1s后执行,每10s执行一次;
longingTaskCount是缓存cacheMap的长度/3000,currentLongingTaskCount初始值是0,当longingTaskCount>currentLongingTaskCount时,拿currentLongingTaskCount做起始数做循环执行ClientWorker.LongPollingRunnable方法。
而此方法中执行了checkLocalConfig(CacheData cacheData)方法。这个方法应该是更新本地缓存文件的方法,通过检查文件状态和cacheData的isUseLocalConfigInfo属性和LocalConfigInfoVersion版本号对比确定要不要更新本地缓存配置文件。
checkListenerMd5方法检查MD5的值是否一致,不一致则表示数据变更,触发safeNotifyListener(…)方法,此方法单独开线程,向所有对dataId注册过监听的客户端推送变更后的数据内容。客户端接收到通知通过receiveConfigInfo(…)方法接受回调数据。