Nacos 客户端启动时自动拉取服务列表
在自动注册时说过,在springBoot项目启动时,会加载nacos相关的spring.factories文件,而自动发现最关键的类在 NacosDiscoveryClientConfiguration 在自动配置类中,名为NacosWatch,下图可以看出,其也会被Spring容器加载。
NacosWatch 实现了 SmartLifeCycle 接口,这是 spring 提供的扩展点之一,IOC容器在初始化结束时会调用所有容器中 SmartLifecycle 实现类的 start 方法
Start 方法中的 namingService.subscribe 方法里会从服务端拉取注册表
@Override
public void start() {
if (this.running.compareAndSet(false, true)) {
// 创建一个监听NamingEvent事件的监听器,加入到listenerMap中
EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
event -> new EventListener() {
@Override
public void onEvent(Event event) {
if (event instanceof NamingEvent) {
List<Instance> instances = ((NamingEvent) event)
.getInstances();
Optional<Instance> instanceOptional = selectCurrentInstance(
instances);
instanceOptional.ifPresent(currentInstance -> {
resetIfNeeded(currentInstance);
});
}
}
});
// 获取namingService
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
try {
// 这里面会从服务端拉取一次注册表
namingService.subscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService subscribe failed, properties:{}", properties, e);
}
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay());
}
}
上面注册监听我们先不讲解,下面的 getServiceInfo 方法才是关键
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
if (failoverReactor.isFailoverSwitch()) {
return failoverReactor.getService(key);
}
// 从缓存中获取该service@@cluster下的服务实例列表
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
if (null == serviceObj) {
serviceObj = new ServiceInfo(serviceName, clusters);
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
// 在多线程的情况下,为了避免太多线程同时在更新注册列表,往updatingMap设置一个值,但是还是不可避免有几个线程同时更新
updatingMap.put(serviceName, new Object());
// 里面会调用server端接口查询服务列表,并更新到serviceInfoMap
updateServiceNow(serviceName, clusters);
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
if (UPDATE_HOLD_INTERVAL > 0) {
// hold a moment waiting for update finish
synchronized (serviceObj) {
try {
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
NAMING_LOGGER
.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" +</