spring-cloud-Alibaba-nacos-discovery

1,685 阅读11分钟

不要问我阅读spring源码有什么用,问就是没有用,只是让我自己使用spring的过程中自信点!

相关文章

spring-相关文章

说明

1. 这个文只是说下注册的流程,nacos-config 在这里是不说的,后面会单独说

大致流程图

上面的图,大致把整个流程分为了三部分
1. spring整合nacos
2. nacos客户端的处理
3. nacos服务端的处理

spring整合nacos

1. 一般spring整合第三方组件,最少会有两个包,一个为 spring-boot/cloud-XXX-starter , 一个为服务核心包,就是一个启动包,一个核心包
2. spring整合nacos服务与发现的 启动包为 spring-cloud-Alibaba-nacoc-discovery
3. 这个包的作用就会注册当前jar的spring.factories中的类到springioc容器,这个jar的注册原理在这不多说了,后面会写一篇<手写spring-boot-XXX-starter及其原理>
//一起要从NacosServiceRegistryAutoConfiguration说起
//NacosServiceRegistryAutoConfiguration是在 spring-cloud-Alibaba-nacoc-discovery 下面的spring.factories中注册的
//com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration.nacosServiceRegistry(NacosDiscoveryProperties)
//创建 nacosServiceRegistry 
@Bean
public NacosServiceRegistry nacosServiceRegistry(
		NacosDiscoveryProperties nacosDiscoveryProperties) {
	return new NacosServiceRegistry(nacosDiscoveryProperties);
}


nacosServiceRegistry包含的属性
    NacosDiscoveryProperties
    NamingService

//这个方法看的出只是给nacosServiceRegistry的两个属性分别赋值
//NacosDiscoveryProperties这个是配置参数类,不看了,应该是@Bean来的
//主要看下NamingService 这个是是nacos组件的类
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
	this.nacosDiscoveryProperties = nacosDiscoveryProperties;
	this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}


public NamingService namingServiceInstance() {
    //假如有直接返回
	if (null != namingService) {
		return namingService;
	}

	try {
	    //没有就去创建
		namingService = NacosFactory.createNamingService(getNacosProperties());
	}
	catch (Exception e) {
		log.error("create naming service error!properties={},e=,", this, e);
		return null;
	}
	return namingService;
}


//getNacosProperties这个方法,里面就是获取一些服务信息


//继续 
 public static NamingService createNamingService(Properties properties) throws NacosException {
    return NamingFactory.createNamingService(properties);
}


//
public static NamingService createNamingService(Properties properties) throws NacosException {
    try {
        //会发现是通过反射创建的
        //获取clss 对象
        Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
        Constructor constructor = driverImplClass.getConstructor(Properties.class);
        //调用有参构造方法
        NamingService vendorImpl = (NamingService)constructor.newInstance(properties);
        return vendorImpl;
    } catch (Throwable e) {
        throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
    }
}

//com.alibaba.nacos.client.naming.NacosNamingService.NacosNamingService(Properties)
 public NacosNamingService(Properties properties) {
    init(properties);
 }
 
 //初始化 
 private void init(Properties properties) {
    //命名空间 public
    //下面的几行代码从名字看是做一些初始化的工作,具体的我也没咋看过
    namespace = InitUtils.initNamespaceForNaming(properties);
    initServerAddr(properties);
    InitUtils.initWebRootContext();
    initCacheDir();
    initLogName(properties);

    eventDispatcher = new EventDispatcher();
    //这里里面有两个定时任务 好像是针对nacos自身服务的,没注意看
    serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
    
    //从名字大致能猜的出来,心跳  里面有一个定时线程池,就是处理 发送心跳的任务的
    //initClientBeatThreadCount方法 是根据机器选择线程数的
    beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
    //这个是处理 服务 服务发生改变的任务
    hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties),
        initPollingThreadCount(properties));
}

nacos客户端


//nacos客户端启动的源头是spring的监听器
//NacosAutoServiceRegistration是在NacosServiceRegistryAutoConfiguration中@bean创建
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
		NacosServiceRegistry registry,
		AutoServiceRegistrationProperties autoServiceRegistrationProperties,
		NacosRegistration registration) {
	return new NacosAutoServiceRegistration(registry,
			autoServiceRegistrationProperties, registration);
}
NacosAutoServiceRegistration(nacos提供)的父类AbstractAutoServiceRegistration(spring-cloud提供)实现了
ApplicationListener<WebServerInitializedEvent> 监听的是Web初始化事件

//监听器的原理这里不多说了,后续有时间会单独写一篇介绍spring监听器原理的文章
//这里只需要知道当监听的事件触发会调用实现类的onApplicationEvent方法即可


@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();
}


public void start() {
	if (!isEnabled()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Discovery Lifecycle disabled. Not starting");
		}
		return;
	}

	// only initialize if nonSecurePort is greater than 0 and it isn't already running
	// because of containerPortInitializer below
	if (!this.running.get()) {
		this.context.publishEvent(
				new InstancePreRegisteredEvent(this, getRegistration()));
		//注册开始
		//这些代码还都是在spring-cloud中执行的,都是spring提供的一些公用接口
		register();
		if (shouldRegisterManagement()) {
			registerManagement();
		}
		this.context.publishEvent(
				new InstanceRegisteredEvent<>(this, getConfiguration()));
		this.running.compareAndSet(false, true);
	}

}


@Override
//当前放在所在位置  NacosAutoServiceRegistration 类
protected void register() {
	if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
		log.debug("Registration disabled.");
		return;
	}
	if (this.registration.getPort() < 0) {
		this.registration.setPort(getPort().get());
	}
	//调用父类的 register 方法
	super.register();
}

//当前放在所在位置 AbstractAutoServiceRegistration 
protected void register() {
	//调用 serviceRegistry 属性的方法
	//上述代码总体来干的事:
	//真正的注册代码是在AbstractAutoServiceRegistration的serviceRegistry属性的register开始的
	//nacos的NacosServiceRegistry类实现了ServiceRegistry接口
	//NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration
	//所以说当前实现类NacosAutoServiceRegistration有一个属性serviceRegistry,这个属性的实现类是nacos提供的NacosServiceRegistry
	//总体来说就是spring提供了一套公用代码和接口,第三方组件实现他们就可以了
	this.serviceRegistry.register(getRegistration());
}

//nacos代码开始
//当前类就是上面讲到的spring整合nacos 的过程中 @bean 注册的 
@Override
public void register(Registration registration) {
    
    //获取serviceId 就是你的服务名
    //所以说假如的你微服务没有设置 spring.application.name 那么你的服务是不能注册到nacos
	if (StringUtils.isEmpty(registration.getServiceId())) {
		log.warn("No service to register for nacos client...");
		return;
	}

	String serviceId = registration.getServiceId();
	String group = nacosDiscoveryProperties.getGroup();
    //封装下实例信息,例如 ip ,端口 之类的
	Instance instance = getNacosInstanceFromRegistration(registration);

	try {
	    //调用 namingService 的注册方法
	    //上面已经讲过 namingService 是通过反射创建的,里面搞了些初始化的信息
	    //例如  发送心跳的线程池就是在init创建的 并且保存在 其属性 BeatReactor...中
		namingService.registerInstance(serviceId, group, instance);
		log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
				instance.getIp(), instance.getPort());
	}
}


@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    //是否临时实例,一般情况都是true
    if (instance.isEphemeral()) {
        //包装了  发送心跳需要的信息 
        BeatInfo beatInfo = new BeatInfo();
        beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
        beatInfo.setIp(instance.getIp());
        beatInfo.setPort(instance.getPort());
        beatInfo.setCluster(instance.getClusterName());
        beatInfo.setWeight(instance.getWeight());
        beatInfo.setMetadata(instance.getMetadata());
        beatInfo.setScheduled(false);
        beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
        //发送心跳的任务创建  ,   我们先看下这个方法
        beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
    }
    
    //主线 注册代码
    serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}

发送心跳任务

public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
	NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
	//获取key  假如服务名为  service-ribbon-test01
	//则key为 DEFAULT_GROUP@@service-ribbon-test01#10.40.40.109#7001
	String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
	BeatInfo existBeat = null;
	//fix #1733
	if ((existBeat = dom2Beat.remove(key)) != null) {
		existBeat.setStopped(true);
	}
	//放入 dom2Beat  说实话不知道干啥,也没有仔细的看过
	dom2Beat.put(key, beatInfo);
	//创建了一个任务 BeatTask  ,  BeatTask是实现了Runnable的 
	//默认是五秒之后执行,这个方法只执行一次,至于为啥能一直发送心跳,是在 BeatTask中处理的
	executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
	MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}

//BeatTask
class BeatTask implements Runnable {

    BeatInfo beatInfo;

    public BeatTask(BeatInfo beatInfo) {
        this.beatInfo = beatInfo;
    }

    @Override
    //只要看下这个方法
    public void run() {
        if (beatInfo.isStopped()) {
            return;
        }
        //记录下间隔时间
        long nextTime = beatInfo.getPeriod();
        try {
            //发送心跳的方法 , 里面就是封装的HTTP请求,不具体看了
            JSONObject result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
            long interval = result.getIntValue("clientBeatInterval");
            boolean lightBeatEnabled = false;
            if (result.containsKey(CommonParams.LIGHT_BEAT_ENABLED)) {
                lightBeatEnabled = result.getBooleanValue(CommonParams.LIGHT_BEAT_ENABLED);
            }
            BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
            if (interval > 0) {
                nextTime = interval;
            }
            int code = NamingResponseCode.OK;
            if (result.containsKey(CommonParams.CODE)) {
                code = result.getIntValue(CommonParams.CODE);
            }
            //这个里面是再次 注册
            if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
                Instance instance = new Instance();
                instance.setPort(beatInfo.getPort());
                instance.setIp(beatInfo.getIp());
                instance.setWeight(beatInfo.getWeight());
                instance.setMetadata(beatInfo.getMetadata());
                instance.setClusterName(beatInfo.getCluster());
                instance.setServiceName(beatInfo.getServiceName());
                instance.setInstanceId(instance.getInstanceId());
                instance.setEphemeral(true);
                try {
                    serverProxy.registerService(beatInfo.getServiceName(),
                        NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
                } catch (Exception ignore) {
                }
            }
        } catch (NacosException ne) {
            NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
                JSON.toJSONString(beatInfo), ne.getErrCode(), ne.getErrMsg());

        }
        //发送心跳 成功了之后  等一个固定时间再次创建 beattask 任务
        executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
    }
}


注册

public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {

    NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
        namespaceId, serviceName, instance);
    //封装请求信息
    final Map<String, String> params = new HashMap<String, String>(9);
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, serviceName);
    params.put(CommonParams.GROUP_NAME, groupName);
    params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
    params.put("ip", instance.getIp());
    params.put("port", String.valueOf(instance.getPort()));
    params.put("weight", String.valueOf(instance.getWeight()));
    params.put("enable", String.valueOf(instance.isEnabled()));
    params.put("healthy", String.valueOf(instance.isHealthy()));
    params.put("ephemeral", String.valueOf(instance.isEphemeral()));
    params.put("metadata", JSON.toJSONString(instance.getMetadata()));
    
    //看到这个方法 大致能猜到就是这里
    reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);

}


//五个参数
//api 请求地址  /nacos/v1/ns/instance
//params  请求参数
//body
//servers  nacos 的地址,之所以是集合,是因为nacos 的集群模式
//method 请求类型  post
public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException {

    params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
    //判断下  nacos服务端 列表 是否存在
    if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
        throw new NacosException(NacosException.INVALID_PARAM, "no server available");
    }
    
    NacosException exception = new NacosException();

    if (servers != null && !servers.isEmpty()) {
        //获取 要注册的 nacos服务端的 地址  
        //规则是随机的
        Random random = new Random(System.currentTimeMillis());
        int index = random.nextInt(servers.size());
        for (int i = 0; i < servers.size(); i++) {
            String server = servers.get(index);
            try {
                //这里
                return callServer(api, params, body, server, method);
            } catch (NacosException e) {
                exception = e;
                if (NAMING_LOGGER.isDebugEnabled()) {
                    NAMING_LOGGER.debug("request {} failed.", server, e);
                }
            }
            index = (index + 1) % servers.size();
        }
    }
    
    
    ............................................................................

}


public String callServer(String api, Map<String, String> params, String body, String curServer, String method)
    throws NacosException {
    //记录下时间
    long start = System.currentTimeMillis();
    long end = 0;
    injectSecurityInfo(params);
    List<String> headers = builderHeaders();

    String url;
    if (curServer.startsWith(UtilAndComs.HTTPS) || curServer.startsWith(UtilAndComs.HTTP)) {
        //请求地址
        url = curServer + api;
    } else {
        if (!curServer.contains(UtilAndComs.SERVER_ADDR_IP_SPLITER)) {
            curServer = curServer + UtilAndComs.SERVER_ADDR_IP_SPLITER + serverPort;
        }
        //拼接请求地址
        //没有http或者https 前缀的
        url = HttpClient.getPrefix() + curServer + api;
    }
    
    //发起http请求  注册结束
    HttpClient.HttpResult result = HttpClient.request(url, headers, params, body, UtilAndComs.ENCODING, method);
    end = System.currentTimeMillis();

    MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(result.code))
        .observe(end - start);

    if (HttpURLConnection.HTTP_OK == result.code) {
        return result.content;
    }

    if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
        return StringUtils.EMPTY;
    }

    throw new NacosException(result.code, result.content);
}

nacos服务端

//上面将nacos客户端的时候我们已经知道了注册的地址为  /nacos/v1/ns/instance
//代码模块为   nacos-naming  代码如下:
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
    //获取服务名称  
    final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    final String namespaceId = WebUtils
            .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    //解析请求参数
    final Instance instance = parseInstance(request);
    //主线逻辑  注册
    serviceManager.registerInstance(namespaceId, serviceName, instance);
    return "ok";
}

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    
    //这个方法要看下,主要是获取存放实例的容器
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    
    //获取服务
    Service service = getService(namespaceId, serviceName);
    
    if (service == null) {
        throw new NacosException(NacosException.INVALID_PARAM,
                "service not found, namespace: " + namespaceId + ", service: " + serviceName);
    }
    //注册服务 也就是添加服务  注册就是添加数据 
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}

创建服务容器

public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
    createServiceIfAbsent(namespaceId, serviceName, local, null);
}


public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
        throws NacosException {
    // 这里,主要方法
    Service service = getService(namespaceId, serviceName);
    if (service == null) {
        //key 为 public  一般情况下,我们的使用的时候  key 都是 public
        //如果为空  就去构建这样一个容器
        //容器结构在文章开始的那张图里面
        Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
        service = new Service();
        service.setName(serviceName);
        service.setNamespaceId(namespaceId);
        service.setGroupName(NamingUtils.getGroupName(serviceName));
        // now validate the service. if failed, exception will be thrown
        //这个是设置下最后修改时间,一般情况下 就是心跳的时间
        service.setLastModifiedMillis(System.currentTimeMillis());
        service.recalculateChecksum();
        if (cluster != null) {
            cluster.setService(service);
            service.getClusterMap().put(cluster.getName(), cluster);
        }
        service.validate();
        
        putServiceAndInit(service);
        if (!local) {
            addOrReplaceService(service);
        }
    }
}

//把创建service 放入 map 容器中
private void putServiceAndInit(Service service) throws NacosException {
    //这个放入map中  key 为 public  namespaceId
    putService(service);
    //初始化 这个其实还挺重要的
    service.init();
    
    //下面两行代码的意思是把你刚刚创建的容器 分别放入ephemeralConsistencyService,persistentConsistencyService 中的 listeners属性中
    //listeners 属性结构为   Map<String, ConcurrentLinkedQueue<RecordListener>> listeners
    //key 为:
    //com.alibaba.nacos.naming.iplist.ephemeral+NamespaceId##gorup@@serviceName
    //com.alibaba.nacos.naming.iplist.+NamespaceId##gorup@@serviceName
    //他们的实现类分别为  DistroConsistencyServiceImpl   RaftConsistencyServiceImpl
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
    Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}


 public void init() {
    //创建 检查实例 是否健康的 任务
    HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
    for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
        entry.getValue().setService(this);
        entry.getValue().init();
    }
}



//
public Service getService(String namespaceId, String serviceName) {
    // serviceMap 的数据结构 Map<String, Map<String, Service>> serviceMap 
    // namespaceId 为 public
    //第一个服务注册,肯定为 null
    if (serviceMap.get(namespaceId) == null) {
        return null;
    }
    return chooseServiceMap(namespaceId).get(serviceName);
}

检查实例是否健康

//这里是接上面的 init 方法

public void init() {
    //创建检查实例是否健康的任务   
    HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
    for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
        entry.getValue().setService(this);
        entry.getValue().init();
    }
}


public static void scheduleCheck(ClientBeatCheckTask task) {
    ///key  value
    futureMap.putIfAbsent(task.taskKey(), GlobalExecutor.scheduleNamingHealth(task, 5000, 5000, TimeUnit.MILLISECONDS));
}



//我们先看下这个任务(ClientBeatCheckTask 实现了 Runnable)的java doc 
//从名字能看出 是 客户端的心跳检查,,,还有个心跳检查任务  HealthCheckTask
//Check and update statues of ephemeral instances, remove them if they have been expired.
//直接在有道 翻译 就能大致了解它的作用:
//检查和更新临时实例的状态,如果它们已经过期,则删除它们

//看下它的 run  方法
@Override
public void run() {
    try {
        if (!getDistroMapper().responsible(service.getName())) {
            return;
        }
        
        if (!getSwitchDomain().isHealthCheckEnabled()) {
            return;
        }
        
        //这个地方是获取所有的实例
        //为啥要加一个boolean参数呢,是因为,我们的实例 分两种 临时的,非临时的,是放在不同的容器总的 
        //非别是 ephemeralInstances  persistentInstances
        List<Instance> instances = service.allIPs(true);
        
        // first set health status of instances:
        //循环获取到的实例  ,  这个for 中的操作是  是标记为不可用
        for (Instance instance : instances) {
            //判断当前时间减去实例最后更新的时间 是否大于默认的 超时时间  默认时间为 15 秒
            if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
                if (!instance.isMarked()) {
                    //假如还是健康
                    if (instance.isHealthy()) {
                        //设置为不健康的
                        instance.setHealthy(false);
                        //打印一波日志
                        Loggers.EVT_LOG
                                .info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client timeout after {}, last beat: {}",
                                        instance.getIp(), instance.getPort(), instance.getClusterName(),
                                        service.getName(), UtilsAndCommons.LOCALHOST_SITE,
                                        instance.getInstanceHeartBeatTimeOut(), instance.getLastBeat());
                        getPushService().serviceChanged(service);
                        //发布事件
                        ApplicationUtils.publishEvent(new InstanceHeartbeatTimeoutEvent(this, instance));
                    }
                }
            }
        }
        
        if (!getGlobalConfig().isExpireInstance()) {
            return;
        }
        
        // then remove obsolete instances:
        //这个for 中的操作是  是删除服务 
        for (Instance instance : instances) {
            
            if (instance.isMarked()) {
                continue;
            }
            //判断当前时间减去实例最后更新的时间 是否大于默认的 超时时间  默认时间为 30 秒
            if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {
                // delete instance
                Loggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(),
                        JacksonUtils.toJson(instance));
                //删除实例, 里面就是封装的 http 请求,去通知nacos 集群中别的nacos服务        
                deleteIp(instance);
            }
        }
        
    } catch (Exception e) {
        Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
    }
    
}

注册主线

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    
    //这个方法要看下,主要是获取存放实例的容器
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    
    //获取服务
    Service service = getService(namespaceId, serviceName);
    
    if (service == null) {
        throw new NacosException(NacosException.INVALID_PARAM,
                "service not found, namespace: " + namespaceId + ", service: " + serviceName);
    }
    //注册服务 也就是添加服务  注册就是添加数据 
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}


public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
        throws NacosException {
    
    String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
    
    //再去获取一次 service
    Service service = getService(namespaceId, serviceName);
    
    //加锁
    synchronized (service) {
        //这个方法很重要,先看下
        //这个方法就是获取当前 服务名的 所有实例
        List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
        
        Instances instances = new Instances();
        instances.setInstanceList(instanceList);
        
        consistencyService.put(key, instances);
    }
}


拷贝数据

/**
     *这个方法得作用从原doc来解释就是 比较并且获取最新的实例集合
     *
     * 我自己得理解就是:
     * 先拿到这个service中所有得实例(这首先你要明白储存实例容器的结构是怎么样的)
     * 在加上这次请求的实例(也有可能是删除,请求参数action 就是标识为删除或者添加,就是注册服务或者删除服务)
     * 复制这份数据返回
     */
    public List<Instance> updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips)
            throws NacosException {
        //第一次注册为空
    	//com.alibaba.nacos.naming.iplist.ephemeral+NamespaceId##gorup@@serviceName
        //com.alibaba.nacos.naming.iplist.+NamespaceId##gorup@@serviceName
        //这个数据在什么地方放入consistencyService后面会将的
        Datum datum = consistencyService
                .get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
        
        //第一次注册为空
        List<Instance> currentIPs = service.allIPs(ephemeral);
        //创建一个map 长度为currentIPs得长度,主要是为了节约内存
        Map<String, Instance> currentInstances = new HashMap<>(currentIPs.size());
        //这个set使用来存放实例id得
        Set<String> currentInstanceIds = Sets.newHashSet();
        
        //假如存在实例,循环
        for (Instance instance : currentIPs) {
        	//拼接key  ip:port  value 为实例对象
            currentInstances.put(instance.toIpAddr(), instance);
            //放入 currentInstanceIds
            currentInstanceIds.add(instance.getInstanceId());
        }
        
        //这是基本就是我们需要返回数据的集合了
        Map<String, Instance> instanceMap;
        
        //判断这个datum中是否存在数据,(其实我不明白为什么不从serviceMap中取值)
        if (datum != null) {
        	//假如有数据,搞一份到instanceMap
            instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);
        } else {
        	//假如不存在就创建一个空的map
            instanceMap = new HashMap<>(ips.length);
        }
        //循环这次请求的 Instance
        for (Instance instance : ips) {
        	//假如service.ClusterMap中不存在这个instance.getClusterName()key(这个service要不就是注册的时候创建的,要不就是getServer获取的)
        	//ClusterMap 是在 service 是实例化的时候创建的
            if (!service.getClusterMap().containsKey(instance.getClusterName())) {
            	//假如不存在,创建Cluster , key 为集群名称
                Cluster cluster = new Cluster(instance.getClusterName(), service);
                //这个初始化里面是一个任务,具体干什么的我还不知道
                cluster.init();
                //把cluster放入service.ClusterMap 
                service.getClusterMap().put(instance.getClusterName(), cluster);
                Loggers.SRV_LOG
                        .warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
                                instance.getClusterName(), instance.toJson());
            }
            //这个地方判断下是不是删除实例
            if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
            	//删除实例  说明以前存在,说明instanceMap中存在,删除掉
                instanceMap.remove(instance.getDatumKey());
            } else {
            	//添加到instanceMap
                instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
                instanceMap.put(instance.getDatumKey(), instance);
            }
            
        }
        //假如这个时候是注册的,并且到现在还是为空的,直接报错了
        if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) {
            throw new IllegalArgumentException(
                    "ip list can not be empty, service: " + service.getName() + ", ip list: " + JacksonUtils
                            .toJson(instanceMap.values()));
        }
        //返回 实例  集合
        return new ArrayList<>(instanceMap.values());
    }

注册主线


public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
        throws NacosException {
    
    String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
    
    //再去获取一次 service
    Service service = getService(namespaceId, serviceName);
    
    //加锁
    synchronized (service) {
        //这个方法很重要,先看下
        //这个方法就是获取当前 服务名的 所有实例
        List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
        
        //这两行代码就是复制下获取 实例集合  。 我没看明白为啥要复制
        Instances instances = new Instances();
        instances.setInstanceList(instanceList);
        
        //这里 主线
        consistencyService.put(key, instances);
    }
}

@Override
public void put(String key, Record value) throws NacosException {
    //mapConsistencyService是根据 是否 是临时实例  获取不同的类型 
    //我们这里看 为临时实例  DistroConsistencyServiceImpl
    mapConsistencyService(key).put(key, value);
}

//这个方法很重要
public void put(String key, Record value) throws NacosException {
    //刷新数据到内存
    onPut(key, value);
    //同步注册数据到nacos集群中的别的nacos服务
    taskDispatcher.addTask(key);
}

刷新注册数据到内存


public void onPut(String key, Record value) {
			//这个value 是   实例集合
          if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
              Datum<Instances> datum = new Datum<>();
              datum.value = (Instances) value;
              datum.key = key;
              datum.timestamp.incrementAndGet();
              //把实例集合组装放入  dataStore
              dataStore.put(key, datum);
          }
			
          if (!listeners.containsKey(key)) {
              return;
          }
		//notifier是个实现了 runbable 的任务 
        //这个方法的作用是把当前时间放入notifier的一个队列属性中
        //然后执行这个任务,从队列中依次拿事件处理
          notifier.addTask(key, ApplyAction.CHANGE);
}

public void addTask(String datumKey, ApplyAction action) {
            //如果这个key存在,直接结束,因为下面处理任务的时候,会把这个key删除
            if (services.containsKey(datumKey) && action == ApplyAction.CHANGE) {
                return;
            }
            if (action == ApplyAction.CHANGE) {
            	//把这次事件的名称存下
                services.put(datumKey, StringUtils.EMPTY);
            }
            //放入队列中
            tasks.offer(Pair.with(datumKey, action));
}


//主要看下 notifier 任务的 run 方法,干了什么
public void run() {
            Loggers.DISTRO.info("distro notifier started");
            
            //这里有一个死循环
            for (; ; ) {
                try {
                	//一直从这个队列中拿数据
                    //这个数据就是我们 上面说到的 放进去的事件
                    Pair<String, ApplyAction> pair = tasks.take();
                    //处理
                    handle(pair);
                } catch (Throwable e) {
                    Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
                }
            }
}



private void handle(Pair<String, ApplyAction> pair) {
            try {
                String datumKey = pair.getValue0();
                ApplyAction action = pair.getValue1();
                //删除 services 中的 key
                services.remove(datumKey);
                
                int count = 0;
                
                if (!listeners.containsKey(datumKey)) {
                    return;
                }
                //这个 listeners 的数据放入的过程,上面有讲
                for (RecordListener listener : listeners.get(datumKey)) {
                    
                    count++;
                    
                    try {
                    	//假如是添加
                        if (action == ApplyAction.CHANGE) {
                        	//注册的处理
                            listener.onChange(datumKey, dataStore.get(datumKey).value);
                            continue;
                        }
                        //假如是删除
                        if (action == ApplyAction.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);
            }
 }
 
 
 @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();
 }
 
 
 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(), 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
            //这个地方  把新的实例 放入 clusterMap 中
            //updateIps 这个方法中还有些代码 不看了  
            List<Instance> entryIPs = entry.getValue();
            clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);
        }
        
        setLastModifiedMillis(System.currentTimeMillis());
        
        //发布
        getPushService().serviceChanged(this);
        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());
        
  }


同步注册数据到nacos集群中的别的nacos服务

//com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.put(String, Record)
public void put(String key, Record value) throws NacosException {
        onPut(key, value);
        //同步到nacos集群中别的服务上
        //具体代码不看了
        taskDispatcher.addTask(key);
}