一.入口
springboot自动装配spring.factories文件自动,装配原理就不说了涉及到ioc源码分析,大家可以自行了解
2.NacosServiceRegistryAutoConfiguration里面会创建一个NacosAutoServiceRegistration开始干活是父类AbstractAutoServiceRegistration,spring在所有容器加载完成会调用监听器(WebServerInitializedEvent web服务)完成 onApplicationEvent方法
调用bind方法 利用cas保证端口一致性达到原子性在调用sart方法调用regiister
register方法获取命名空间,封装服务实例 调用namingService.registerInstance
NacosNamingService的registerInstance 心跳处理(心跳源码再说)和向服务端发送请求 /nacos/v1/ns/instance
总结:客户端就是在wen服务初始化完成springLister广播事件直接去发请求/nacos/v1/ns/instance给服务端。由此可见Nacos客户端就是利用监听器去发送请求给服务端达到自动的目的。
二.服务端:
InstanceController接收客户端请求/nacos/v1/ns/instance
registerInstance方法 首先创建一个服务实例,nacos服务列表是一个map,这里拿不到就会初始化一个会涉及到服务发现内容
private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
addInstance方法 获取更新列表使用copyandwrite思想保证cp(将源数据实列拷贝一份,进行增删改,直接复制给源对象)
consistencyService.put(key, instances)是根据临时实例还是永久实例,一般是临时实例
这里涉及到cap 永久实例用的是cp (cowndownLatch) 临时实例用是ap(copyonwriter算法)
#false为永久实例,true表示临时实例开启,注册为临时实例
spring.cloud.nacos.discovery.ephemeral=false
下面涉及到线程池比较分散 以临时实例为例
三.DistroConsistencyServiceImpl
onput 方法里面调用notifier.addTask放入阻塞队列,这里的 notifier维护了一个阻塞队列,并且基于线程池异步执行队列中的任务 集群同步以后再说
Notifier是一个内部类addTask放入阻塞队列
Notifier也是一个Runnable ,run方法执行任务 ,通过一个单线程的线程池来不断从阻塞队列中获取任务,执行服务列表的更新
handle更新服务列表( updateIPs方法调用clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral); 就是copyonWriter)和删除服务列表
private void handle(Pair<String, DataOperation> pair) {
try {
String datumKey = pair.getValue0();
DataOperation action = pair.getValue1();
services.remove(datumKey);
int count = 0;
if (!listeners.containsKey(datumKey)) {
return;
}
// 遍历,找到变化的service,这里的 RecordListener就是 Service
for (RecordListener listener : listeners.get(datumKey)) {
count++;
try {
// 服务的实例列表CHANGE事件
if (action == DataOperation.CHANGE) {
// 更新服务列表
listener.onChange(datumKey, dataStore.get(datumKey).value);
continue;
}
// 服务的实例列表 DELETE 事件
if (action == DataOperation.DELETE) {
listener.onDelete(datumKey);
continue;
}
} catch (Throwable e) {
Loggers.DISTRO.error("[NACOS-DISTRO] error while notifying listener of key: {}", datumKey, e);
}
}
if (Loggers.DISTRO.isDebugEnabled()) {
Loggers.DISTRO
.debug("[NACOS-DISTRO] datum change notified, key: {}, listener count: {}, action: {}",
datumKey, count, action.name());
}
} catch (Throwable e) {
Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
}
}
}
总结:服务端临时实例调用Notifier的run方法对服务列表进行维护,通过一个单线程的线程池来不断从阻塞队列中获取任务定时刷新服务列表