nacos-discovery源碼分析

首先還是Spring.factories中的AutoConfiguration類

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration,\
  org.springframework.cloud.alibaba.nacos.ribbon.RibbonNacosAutoConfiguration,\
  org.springframework.cloud.alibaba.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  org.springframework.cloud.alibaba.nacos.discovery.NacosDiscoveryClientAutoConfiguration

註冊類NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration>,追蹤代碼發現調用start(),再次後register()

	@Override
	@SuppressWarnings("deprecation")
	public void onApplicationEvent(WebServerInitializedEvent event) {
		bind(event);
	}

	@Deprecated
	public void bind(WebServerInitializedEvent event) {
		ApplicationContext context = event.getApplicationContext();
		if (context instanceof ConfigurableWebServerApplicationContext) {
			if ("management".equals(
					((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
				return;
			}
		}
		this.port.compareAndSet(0, event.getWebServer().getPort());
		this.start();
	}

之後會調用client中NacosNamingService的registerInstance(),beatReactor加入心跳,serverProxy註冊到服務器,請求路徑是

/nacos/v1/ns/instance 的post方法

根據路徑找到服務器naming模塊中的InstanceController

之後是交給了serviceManager進行註冊

createEmptyService方法進行了service的創建,service.init()進行了HealthCheckReactor健康檢查,同時putService(service),放入serviceMap
    public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
        Service service = getService(namespaceId, serviceName);
        if (service == null) {

            Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
            service = new Service();
            service.setName(serviceName);
            service.setNamespaceId(namespaceId);
            service.setGroupName(Constants.DEFAULT_GROUP);
            // now validate the service. if failed, exception will be thrown
            service.setLastModifiedMillis(System.currentTimeMillis());
            service.recalculateChecksum();
            service.validate();
            if (local) {
                putService(service);
                service.init();
                consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
                consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
            } else {
                addOrReplaceService(service);
            }
        }
    }
addInstance()方法繼續追蹤到DelegateConsistencyServiceImpl適配器的put方法,最後是implements  EphemeralConsistencyService的DistroConsistencyServiceImpl的put方法

在onput方法中,Notifier加入Task,繼續看Notifier中,回調了RecordListener的具體對應實現類的onChange方法

看誰實現了RecordListener類,ServiceManager,Service等都實現了。

在註冊或者修改時,都是觸發回調了service類的onchange方法

@Override
    public void onChange(String key, Instances value) throws Exception {

        Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);

        for (Instance instance : value.getInstanceList()) {

            if (instance == null) {
                // Reject this abnormal instance list:
                throw new RuntimeException("got null instance " + key);
            }

            if (instance.getWeight() > 10000.0D) {
                instance.setWeight(10000.0D);
            }

            if (instance.getWeight() < 0.01D && instance.getWeight() > 0.0D) {
                instance.setWeight(0.01D);
            }
        }

        updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));

        recalculateChecksum();
    }

updateIPs,進行instance,Cluster的更新,PushService.serviceChanged進行信息同步,udpSocket的方式

public void updateIPs(Collection<Instance> instances, boolean ephemeral) {
        Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());
        for (String clusterName : clusterMap.keySet()) {
            ipMap.put(clusterName, new ArrayList<>());
        }

        for (Instance instance : instances) {
            try {
                if (instance == null) {
                    Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null");
                    continue;
                }

                if (StringUtils.isEmpty(instance.getClusterName())) {
                    instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
                }

                if (!clusterMap.containsKey(instance.getClusterName())) {
                    Loggers.SRV_LOG.warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
                        instance.getClusterName(), instance.toJSON());
                    Cluster cluster = new Cluster(instance.getClusterName());
                    cluster.setService(this);
                    cluster.init();
                    getClusterMap().put(instance.getClusterName(), cluster);
                }

                List<Instance> clusterIPs = ipMap.get(instance.getClusterName());
                if (clusterIPs == null) {
                    clusterIPs = new LinkedList<>();
                    ipMap.put(instance.getClusterName(), clusterIPs);
                }

                clusterIPs.add(instance);
            } catch (Exception e) {
                Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + instance, e);
            }
        }

        for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {
            //make every ip mine
            List<Instance> entryIPs = entry.getValue();
            clusterMap.get(entry.getKey()).updateIPs(entryIPs, ephemeral);
        }

        setLastModifiedMillis(System.currentTimeMillis());
        getPushService().serviceChanged(namespaceId, getName());
        StringBuilder stringBuilder = new StringBuilder();

        for (Instance instance : allIPs()) {
            stringBuilder.append(instance.toIPAddr()).append("_").append(instance.isHealthy()).append(",");
        }

        Loggers.EVT_LOG.info("[IP-UPDATED] namespace: {}, service: {}, ips: {}",
            getNamespaceId(), getName(), stringBuilder.toString());

    }

註冊,就先到這裏。再看心跳檢測,主要是NacosWatch中

NamingService namingService = properties.namingServiceInstance();

			ListView<String> listView = properties.namingServiceInstance()
					.getServicesOfServer(1, Integer.MAX_VALUE);

			List<String> serviceList = listView.getData();

			// if there are new services found, publish event
			Set<String> currentServices = new HashSet<>(serviceList);
			currentServices.removeAll(cacheServices);
			if (currentServices.size() > 0) {
				changed = true;
			}

			// if some services disappear, publish event
			if (cacheServices.removeAll(new HashSet<>(serviceList))
					&& cacheServices.size() > 0) {
				changed = true;

				for (String serviceName : cacheServices) {
					namingService.unsubscribe(serviceName,
							subscribeListeners.get(serviceName));
					subscribeListeners.remove(serviceName);
				}
			}

			cacheServices = new HashSet<>(serviceList);

			// subscribe services's node change, publish event if nodes changed
			for (String serviceName : cacheServices) {
				if (!subscribeListeners.containsKey(serviceName)) {
					EventListener eventListener = event -> NacosWatch.this.publisher
							.publishEvent(new HeartbeatEvent(NacosWatch.this,
									nacosWatchIndex.getAndIncrement()));
					subscribeListeners.put(serviceName, eventListener);
					namingService.subscribe(serviceName, eventListener);

				}
			}

			if (changed) {
				this.publisher.publishEvent(
						new HeartbeatEvent(this, nacosWatchIndex.getAndIncrement()));
			}

獲取服務端的serviceName結合,與本地進行比對,更新本地,有變化時進行相關的訂閱或者取消訂閱,然後發佈event。獲取服務器追蹤後代碼爲獲取namespace的service

List<String> serviceNameList = serviceManager.getAllServiceNameList(namespaceId);

        JSONObject result = new JSONObject();

        if (serviceNameList == null || serviceNameList.isEmpty()) {
            result.put("doms", new ArrayList<String>(1));
            result.put("count", 0);
            return result;
        }

        Iterator<String> iterator = serviceNameList.iterator();

        while (iterator.hasNext()) {
            String serviceName = iterator.next();
            if (!serviceName.startsWith(groupName + Constants.SERVICE_INFO_SPLITER)) {
                iterator.remove();
            }
        }

之後看服務發現,服務發現最後主要是獲取ServerList,nacos中是NacosServerList類,查詢所有健康的instance

private List<NacosServer> getServers() {
		try {
			List<Instance> instances = discoveryProperties.namingServiceInstance()
					.selectInstances(serviceId, true);
			return instancesToServerList(instances);
		}
		catch (Exception e) {
			throw new IllegalStateException(
					"Can not get service instances from nacos, serviceId=" + serviceId,
					e);
		}
	}

	private List<NacosServer> instancesToServerList(List<Instance> instances) {
		List<NacosServer> result = new ArrayList<>();
		if (null == instances) {
			return result;
		}
		for (Instance instance : instances) {
			result.add(new NacosServer(instance));
		}

		return result;
	}

追蹤selectInstances,到HostReactor中

如果serviceInfoMap中獲取不到,則維護一個serviceInfo到serviceInfoMap  updatingMap

如果serviceInfoMap,updatingMap均獲取到了,則wait 5秒

最後是scheduleUpdateIfAbsent,維護到futureMap,同時添加一個UpdateTask到定時任務中了

public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
        if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
            return;
        }

        synchronized (futureMap) {
            if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
                return;
            }

            ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
            futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
        }
    }

UpdateTask的 run

public void run() {
            try {
                ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));

                if (serviceObj == null) {
                    updateServiceNow(serviceName, clusters);
                    executor.schedule(this, DEFAULT_DELAY, TimeUnit.MILLISECONDS);
                    return;
                }

                if (serviceObj.getLastRefTime() <= lastRefTime) {
                    updateServiceNow(serviceName, clusters);
                    serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
                } else {
                    // if serviceName already updated by push, we should not override it
                    // since the push data may be different from pull through force push
                    refreshOnly(serviceName, clusters);
                }

                executor.schedule(this, serviceObj.getCacheMillis(), TimeUnit.MILLISECONDS);

                lastRefTime = serviceObj.getLastRefTime();
            } catch (Throwable e) {
                NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
            }

        }

如果serviceInfo不存在,或者不是最新的,則通過updateServiceNow去獲取

   public void updateServiceNow(String serviceName, String clusters) {
        ServiceInfo oldService = getSerivceInfo0(serviceName, clusters);
        try {

            String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false);
            if (StringUtils.isNotEmpty(result)) {
                processServiceJSON(result);
            }
        } catch (Exception e) {
            NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
        } finally {
            if (oldService != null) {
                synchronized (oldService) {
                    oldService.notifyAll();
                }
            }
        }
    }

之前上面serviceInfoMap在getServiceInfo時如果不存在則添加,同時也通過定時任務進行獲取,更新serviceInfoMap值。

 

後續更多nacos服務端的設計以及實現原理,後續再補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章