nacos registration process

Registration sequence diagram

Insert image description here

Source code tracking

@EnableDiscoveryClient annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
    
    

	/**
	 * If true, the ServiceRegistry will automatically register the local server.
	 */
	boolean autoRegister() default true;
}
  • EnableDiscoveryClient refers to the EnableDiscoveryClientImportSelector class
/**
 * @author Spencer Gibb
 */
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
		extends SpringFactoryImportSelector<EnableDiscoveryClient> {
    
    

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
    
    
		String[] imports = super.selectImports(metadata);

		AnnotationAttributes attributes = AnnotationAttributes.fromMap(
				metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));

		boolean autoRegister = attributes.getBoolean("autoRegister");

		if (autoRegister) {
    
    
			List<String> importsList = new ArrayList<>(Arrays.asList(imports));
			importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
			imports = importsList.toArray(new String[0]);
		} else {
    
    
			Environment env = getEnvironment();
			if(ConfigurableEnvironment.class.isInstance(env)) {
    
    
				ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
				LinkedHashMap<String, Object> map = new LinkedHashMap<>();
				map.put("spring.cloud.service-registry.auto-registration.enabled", false);
				MapPropertySource propertySource = new MapPropertySource(
						"springCloudDiscoveryClient", map);
				configEnv.getPropertySources().addLast(propertySource);
			}

		}

		return imports;
	}

	@Override
	protected boolean isEnabled() {
    
    
		return getEnvironment().getProperty(
				"spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
	}

	@Override
	protected boolean hasDefaultFactory() {
    
    
		return true;
	}

}

  • After turning on automatic service registration, the DiscoveryAutoConfiguration configuration class in the spring-cloud-alibaba-nacos-discovery2.1.0.RELEASE-sources.jar!\META-INF\spring.factories file will be loaded, and automatic registration of nacos services will be turned on.
    Insert image description here

nacos service registration

  • NacosDiscoveryAutoConfiguration
/**
 * @author xiaojing
 * @author <a href="mailto:[email protected]">Mercy</a>
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({
    
     AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration {
    
    
     //声明向注册中⼼注册服务的bean
	@Bean
	public NacosServiceRegistry nacosServiceRegistry(
			NacosDiscoveryProperties nacosDiscoveryProperties) {
    
    
		return new NacosServiceRegistry(nacosDiscoveryProperties);
	}
    //声明存储nacos服务信息的bean
	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosRegistration nacosRegistration(
			NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
    
    
		return new NacosRegistration(nacosDiscoveryProperties, context);
	}
    // 声明⽤于nacos服务⾃动注册的bean
	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosAutoServiceRegistration nacosAutoServiceRegistration(
			NacosServiceRegistry registry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			NacosRegistration registration) {
    
    
		return new NacosAutoServiceRegistration(registry,
				autoServiceRegistrationProperties, registration);
	}
}
  • NacosAutoServiceRegistration inherits AbstractAutoServiceRegistration, and AbstractAutoServiceRegistration implements ApplicationListener monitoring, so the onApplicationEvent method will be executed.
public abstract class AbstractAutoServiceRegistration<R extends Registration>
		implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    
    
	*******************************************

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

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
    
    
		this.context = applicationContext;
		this.environment = this.context.getEnvironment();
	}

	@Deprecated
	protected Environment getEnvironment() {
    
    
		return environment;
	}

	@Deprecated
	protected AtomicInteger getPort() {
    
    
		return port;
	}

	public boolean isAutoStartup() {
    
    
		return this.autoStartup;
	}

	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()));
			register();
			if (shouldRegisterManagement()) {
    
    
				registerManagement();
			}
			this.context.publishEvent(
					new InstanceRegisteredEvent<>(this, getConfiguration()));
			this.running.compareAndSet(false, true);
		}

	}
*******************************************
  • registerInstance method of NacosNamingService
@Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    
    

        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);
            long instanceInterval = instance.getInstanceHeartBeatInterval();
            beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
             //添加⼼跳上报线程
            beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
        }
        //注册服务
        serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
    }

addBeatInfo method

    public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
    
    
        NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
        dom2Beat.put(buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()), beatInfo);
        executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS);
        MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
    }
 class BeatTask implements Runnable {
    
    

        BeatInfo beatInfo;

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

        @Override
        public void run() {
    
    
            if (beatInfo.isStopped()) {
    
    
                return;
            }
            long result = serverProxy.sendBeat(beatInfo);
            long nextTime = result > 0 ? result : beatInfo.getPeriod();
            executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
        }
    }

registerService method

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);

    }
  • InstanceController class
@RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance")
public class InstanceController {
    
    

 //******************************************************************************


    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
    
    

        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);

        serviceManager.registerInstance(namespaceId, serviceName, parseInstance(request));
        return "ok";
    }




 

    
    @GetMapping("/list")
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
    public JSONObject list(HttpServletRequest request) throws Exception {
    
    

        String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
            Constants.DEFAULT_NAMESPACE_ID);

        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        String agent = WebUtils.getUserAgent(request);
        String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
        String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
        Integer udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
        String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
        boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));

        String app = WebUtils.optional(request, "app", StringUtils.EMPTY);

        String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);

        boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));

        return doSrvIPXT(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
            healthyOnly);
    }

    

    @CanDistro
    @PutMapping("/beat")
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public JSONObject beat(HttpServletRequest request) throws Exception {
    
    

        JSONObject result = new JSONObject();

        result.put("clientBeatInterval", switchDomain.getClientBeatInterval());
        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
            Constants.DEFAULT_NAMESPACE_ID);
        String clusterName = WebUtils.optional(request, CommonParams.CLUSTER_NAME,
            UtilsAndCommons.DEFAULT_CLUSTER_NAME);
        String ip = WebUtils.optional(request, "ip", StringUtils.EMPTY);
        int port = Integer.parseInt(WebUtils.optional(request, "port", "0"));
        String beat = WebUtils.optional(request, "beat", StringUtils.EMPTY);

        RsInfo clientBeat = null;
        if (StringUtils.isNotBlank(beat)) {
    
    
            clientBeat = JSON.parseObject(beat, RsInfo.class);
        }

        if (clientBeat != null) {
    
    
            if (StringUtils.isNotBlank(clientBeat.getCluster())) {
    
    
                clusterName = clientBeat.getCluster();
            }
            ip = clientBeat.getIp();
            port = clientBeat.getPort();
        }

        if (Loggers.SRV_LOG.isDebugEnabled()) {
    
    
            Loggers.SRV_LOG.debug("[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}", clientBeat, serviceName);
        }

        Instance instance = serviceManager.getInstance(namespaceId, serviceName, clusterName, ip, port);

        if (instance == null) {
    
    
            if (clientBeat == null) {
    
    
                result.put(CommonParams.CODE, NamingResponseCode.RESOURCE_NOT_FOUND);
                return result;
            }
            instance = new Instance();
            instance.setPort(clientBeat.getPort());
            instance.setIp(clientBeat.getIp());
            instance.setWeight(clientBeat.getWeight());
            instance.setMetadata(clientBeat.getMetadata());
            instance.setClusterName(clusterName);
            instance.setServiceName(serviceName);
            instance.setInstanceId(instance.getInstanceId());
            instance.setEphemeral(clientBeat.isEphemeral());

            serviceManager.registerInstance(namespaceId, serviceName, instance);
        }

        Service service = serviceManager.getService(namespaceId, serviceName);

        if (service == null) {
    
    
            throw new NacosException(NacosException.SERVER_ERROR,
                "service not found: " + serviceName + "@" + namespaceId);
        }
        if (clientBeat == null) {
    
    
            clientBeat = new RsInfo();
            clientBeat.setIp(ip);
            clientBeat.setPort(port);
            clientBeat.setCluster(clusterName);
        }
        service.processClientBeat(clientBeat);

        result.put(CommonParams.CODE, NamingResponseCode.OK);
        result.put("clientBeatInterval", instance.getInstanceHeartBeatInterval());
        result.put(SwitchEntry.LIGHT_BEAT_ENABLED, switchDomain.isLightBeatEnabled());
        return result;
    }

  • ClientBeatCheckTask class client heartbeat check thread
   @Override
    public void run() {
    
    
        try {
    
    
            if (!getDistroMapper().responsible(service.getName())) {
    
    
                return;
            }

            if (!getSwitchDomain().isHealthCheckEnabled()) {
    
    
                return;
            }
            //1. 获取实例
            List<Instance> instances = service.allIPs(true);

            // first set health status of instances:
            //2.检查客户端实例最后使⽤时间是否超时
            for (Instance instance : instances) {
    
    
                if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
    
    
                    if (!instance.isMarked()) {
    
    
                        if (instance.isHealthy()) {
    
    
                        //3.如果超时15秒设置健康状态为false
                            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);
                            SpringContext.getAppContext().publishEvent(new InstanceHeartbeatTimeoutEvent(this, instance));
                        }
                    }
                }
            }

            if (!getGlobalConfig().isExpireInstance()) {
    
    
                return;
            }

            // then remove obsolete instances:
            for (Instance instance : instances) {
    
    

                if (instance.isMarked()) {
    
    
                    continue;
                }
// 4. 检查是否超过30秒
                if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {
    
    
                    // delete instance
                    // 5. 如果超过30秒则删除实例
                    Loggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(), JSON.toJSONString(instance));
                    deleteIP(instance);
                }
            }

        } catch (Exception e) {
    
    
            Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
        }

    }
  • ClientBeatProcessor client heartbeat processing class
@Override
    public void run() {
    
    
        Service service = this.service;
        if (Loggers.EVT_LOG.isDebugEnabled()) {
    
    
            Loggers.EVT_LOG.debug("[CLIENT-BEAT] processing beat: {}", rsInfo.toString());
        }

        String ip = rsInfo.getIp();
        String clusterName = rsInfo.getCluster();
        int port = rsInfo.getPort();
        Cluster cluster = service.getClusterMap().get(clusterName);
        //1.获取所有实例
        List<Instance> instances = cluster.allIPs(true);

        for (Instance instance : instances) {
    
    
            if (instance.getIp().equals(ip) && instance.getPort() == port) {
    
    
                if (Loggers.EVT_LOG.isDebugEnabled()) {
    
    
                    Loggers.EVT_LOG.debug("[CLIENT-BEAT] refresh beat: {}", rsInfo.toString());
                }
                //2. 设置实例的最后使⽤时间
                instance.setLastBeat(System.currentTimeMillis());
                if (!instance.isMarked()) {
    
    
                    if (!instance.isHealthy()) {
    
    
                        instance.setHealthy(true);
                        Loggers.EVT_LOG.info("service: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: client beat ok",
                            cluster.getService().getName(), ip, port, cluster.getName(), UtilsAndCommons.LOCALHOST_SITE);
                        getPushService().serviceChanged(service);
                    }
                }
            }
        }
    }

Summarize

  1. NacosDiscoveryAutoConfiguration defines 3 beans for the registration process entry class
  • NacosServiceRegistry declares the bean that registers the service with the registration center
  • NacosRegistration declares a bean that stores nacos service information
  • NacosAutoServiceRegistration declares beans used for nacos service automatic registration
  1. NacosAutoServiceRegistration inherits AbstractAutoServiceRegistration. AbstractAutoServiceRegistration implements ApplicationListener monitoring, so it will execute the onApplicationEvent method.
  • onApplicationEvent calls the bind method –> calls this.start();
  1. The onApplicationEvent method finally calls–>NacosServiceRegistry.register–>NacosNamingService.registerInstance for service registration.
  • beatReactor.addBeatInfo() turns on the delay thread to report heartbeat. reqApi("nacos/v1/ns/instance/beat")
  • serverProxy.registerService() method for service registration, reqApi("nacos/v1/ns/instance")
  1. beatReactor.addBeatInfo() and serverProxy.registerService() ultimately call the beat and register methods of InstanceController in nacos to perform heartbeat reporting and service registration respectively.
  • beat method

    • Perform client heartbeat detection and modify the last usage time and set the health status to true
  • register method

    • Create an empty service
    • Create and initialize an empty service
    • Call the init method for heartbeat detection 15 seconds <0 <30 seconds to modify the instance health status to false, > 30 seconds to delete the service

Guess you like

Origin blog.csdn.net/u014535922/article/details/131056502