Nacos自动注册流程源码分析

根据SpringBoot的自动装配规则,联想到nacos实现自动注册肯定有一个XXXAutoConfiguration这个类在实现

查看NacosCloud 源码的META-INF

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

查看META-INF 发现跟自动注册相关和自动配置类NacosDiscoveryAutoConfiguration 和 NacosServiceRegistryAutoConfiguration比较符合我们需求分别查看源码

NacosDiscoveryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public NacosDiscoveryProperties nacosProperties() {
		return new NacosDiscoveryProperties();
	}

	@Bean
	@ConditionalOnMissingBean
	public NacosServiceDiscovery nacosServiceDiscovery(
			NacosDiscoveryProperties discoveryProperties) {
		return new NacosServiceDiscovery(discoveryProperties);
	}

}
  • 该类获取NacosDiscoveryProperties 用于绑定了我们在配置文件中的spring.cloud.nacos.discovery的配置

  • NacosServiceDiscovery 封装了 通过Properties 的配置 底层调用nacos api来创建 获取ServiceInstance这里的ServiceInstance是底层Spring Cloud定义的顶层接口规范

  • NacosServiceDiscovery是封装了获取Spring Cloud 的顶层ServiceInstance ServerName的工具包

    /**
    	 * Return all instances for the given service.
    	 * @param serviceId id of service
    	 * @return list of instances
    	 * @throws NacosException nacosException
    	 */
    	public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
    		String group = discoveryProperties.getGroup();
    		List<Instance> instances = discoveryProperties.namingServiceInstance()
    				.selectInstances(serviceId, group, true);
    		return hostToServiceInstanceList(instances, serviceId);
    	}
    
    	public NamingService namingServiceInstance() {
    
    		if (null != namingService) {
    			return namingService;
    		}
    
    		try {
                // 底层nacosFacactory 就是nacos 核心 API包中的数据
    			namingService = NacosFactory.createNamingService(getNacosProperties());
    		}
    		catch (Exception e) {
    			log.error("create naming service error!properties={},e=,", this, e);
    			return null;
    		}
    		return namingService;
    	}
    

核心查看NacosServiceRegistryAutoConfiguration 类

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
		matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class,
		NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {

    // NacosServiceRegistry implements ServiceRegistry<Registration>
    // 实现了顶层Spring Cloud 的 ServiceRegistry接口用来进行服务的注册
    // 其中的com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register 方法是真正调用nacos API将服务注册到nacos-server的逻辑
	@Bean
	public NacosServiceRegistry nacosServiceRegistry(
			NacosDiscoveryProperties nacosDiscoveryProperties) {
		return new NacosServiceRegistry(nacosDiscoveryProperties);
	}

    //  NacosRegistration implements Registration, ServiceInstance 
    // 里面包装了注册需要的一些配置信息比如当前节点的ip port 权重 metaData等 和 Spring 上下文绑定做一些初始化操作
	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosRegistration nacosRegistration(
			NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
		return new NacosRegistration(nacosDiscoveryProperties, context);
	}

    // 实现自动注册核心类 聚合了registry autoServiceRegistrationProperties registration 注册需要的工具 和 配置属性
	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosAutoServiceRegistration nacosAutoServiceRegistration(
			NacosServiceRegistry registry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			NacosRegistration registration) {
		return new NacosAutoServiceRegistration(registry,
				autoServiceRegistrationProperties, registration);
	}

}

NacosAutoServiceRegistration 类分析

public class NacosAutoServiceRegistration
		extends AbstractAutoServiceRegistration<Registration> {

	private static final Logger log = LoggerFactory
			.getLogger(NacosAutoServiceRegistration.class);

	private NacosRegistration registration;

	public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			NacosRegistration registration) {
		super(serviceRegistry, autoServiceRegistrationProperties);
		this.registration = registration;
	}

	@Deprecated
	public void setPort(int port) {
		getPort().set(port);
	}

	@Override
	protected NacosRegistration getRegistration() {
		if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
			this.registration.setPort(this.getPort().get());
		}
		Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
		return this.registration;
	}

	@Override
	protected NacosRegistration getManagementRegistration() {
		return null;
	}

	@Override
	protected void register() {
		if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
			log.debug("Registration disabled.");
			return;
		}
		if (this.registration.getPort() < 0) {
			this.registration.setPort(getPort().get());
		}
		super.register();
	}

	@Override
	protected void registerManagement() {
		if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
			return;
		}
		super.registerManagement();

	}

	@Override
	protected Object getConfiguration() {
		return this.registration.getNacosDiscoveryProperties();
	}

	@Override
	protected boolean isEnabled() {
		return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
	}

	@Override
	@SuppressWarnings("deprecation")
	protected String getAppName() {
		String appName = registration.getNacosDiscoveryProperties().getService();
		return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
	}

}

  • 首先NacosAutoServiceRegistration继承了org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration 这个类是SpringCloud 定义的顶层抽象规范

    public abstract class AbstractAutoServiceRegistration<R extends Registration>
          implements AutoServiceRegistration, ApplicationContextAware,
          ApplicationListener<WebServerInitializedEvent>
    
    • 该抽象类 实现了ApplicationListener 方法在Spring容器发布WebServerInitializedEvent 事件的时候会调用他的onApplicationEvent(WebServerInitializedEvent event)方法 完成服务注册功能

      	@Override
      	@SuppressWarnings("deprecation")
      	public void onApplicationEvent(WebServerInitializedEvent event) {
      		bind(event);
      	}
      
    • 查看bind里面 拿到Server的启动端口号 和调用了this.start方法 进一步分析start方法

      	@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();
      	}
      
    • start方法 核心调用了 register方法实现注册逻辑 然后发布了2个注册相关前后的事件

      	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);
      		}
      
      	}
      
    • register方法 底层就调用了registry的register方法

      	protected void register() {
      		this.serviceRegistry.register(getRegistration());
      	}
      
    • 这个serverRegistry 是org.springframework.cloud.client.serviceregistry.ServiceRegistry类型是Spring Cloud的底层规范 发现只有一个类实现了该接口NacosServiceRegistry 本质上调用了该类的register方法 真正完成将服务信息注册到nacos-server上

      	public void register(Registration registration) {
      
      		if (StringUtils.isEmpty(registration.getServiceId())) {
      			log.warn("No service to register for nacos client...");
      			return;
      		}
      
      		String serviceId = registration.getServiceId();
      		String group = nacosDiscoveryProperties.getGroup();
      
      		Instance instance = getNacosInstanceFromRegistration(registration);
      
      		try {
      			namingService.registerInstance(serviceId, group, instance);
      			log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
      					instance.getIp(), instance.getPort());
      		}
      		catch (Exception e) {
      			log.error("nacos registry, {} register failed...{},", serviceId,
      					registration.toString(), e);
      			// rethrow a RuntimeException if the registration is failed.
      			// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
      			rethrowRuntimeException(e);
      		}
      	}
      
  • 现在回到NacosAutoServiceRegistration 类 AbstractAutoServiceRegistration是抽象类最终是调用到了NacosAutoServiceRegistration .register 方法

    	protected void register() {
    		if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
    			log.debug("Registration disabled.");
    			return;
    		}
    		if (this.registration.getPort() < 0) {
    			this.registration.setPort(getPort().get());
    		}
    		super.register();
    	}
    
  • NacosAutoServiceRegistration 最核心的就是调用配super.register(); 调用了父类的方法

    	protected void register() {
    		this.serviceRegistry.register(getRegistration());
    	}
    
  • 父类的方法调用了serviceRegistry.register方法 这里的registry 其实就是实例化NacosAutoServiceRegistration 传入构造器的registry

    image-20210524180637839

  • 底层就来到了com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register方法

  • 进一步分析该类的register 方法

    	@Override
    	public void register(Registration registration) {
    
    		if (StringUtils.isEmpty(registration.getServiceId())) {
    			log.warn("No service to register for nacos client...");
    			return;
    		}
    		// 在NacosAutoServiceRegistration 已经聚合registration 获取注册的ServiceInstance的Id
    		String serviceId = registration.getServiceId();
            // 获取用户配置的groupId
    		String group = nacosDiscoveryProperties.getGroup();
    		// 将registration 中的配置信息保存到Instance中封装成SC的标准Instance
            /**
            	private Instance getNacosInstanceFromRegistration(Registration registration) {
    		Instance instance = new Instance();
    		instance.setIp(registration.getHost());
    		instance.setPort(registration.getPort());
    		instance.setWeight(nacosDiscoveryProperties.getWeight());
    		instance.setClusterName(nacosDiscoveryProperties.getClusterName());
    		instance.setMetadata(registration.getMetadata());
    		return instance;
    	}
            */
    		Instance instance = getNacosInstanceFromRegistration(registration);
    
    		try {
                // 正在实现注册到nacos上的是调用了namingService的reigsterInstance方法注册到nacos-server	
    			namingService.registerInstance(serviceId, group, instance);
    			log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
    					instance.getIp(), instance.getPort());
    		}
    		catch (Exception e) {
    			log.error("nacos registry, {} register failed...{},", serviceId,
    					registration.toString(), e);
    			// rethrow a RuntimeException if the registration is failed.
    			// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
    			rethrowRuntimeException(e);
    		}
    	}
    
  • 分析namingService.registerInstance方法 namingService已经从Spring Cloud Alibaba Nacos项目走到Nacos Client包中了

        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);
                beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
    
                beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
            }
            // 注册逻辑
            serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
        }
    
  • serverProxy.registerService 实现注册逻辑

  • 进一步分析registerService方法 本质上是封装了各种请求参数 然后发送一个POST请求到nacos-server上完成注册

        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()));
    		// 本质上发送了一个HTTP 的Post请求到Nacos-server
            // UtilAndComs.NACOS_URL_INSTANCE = /nacos/v1/ns/instance
            reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
    
        }
    
  • 查看reqAPI底层源码 本质上对所有nacos-server 随机选择一台发送POST请求 调用callServer方法来发送请求 即可完整服务自动注册

        public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException {
    
            params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
    
            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()) {
    
                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();
                }
            }
    
            if (StringUtils.isNotBlank(nacosDomain)) {
                for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
                    try {
                        return callServer(api, params, body, nacosDomain, method);
                    } catch (NacosException e) {
                        exception = e;
                        if (NAMING_LOGGER.isDebugEnabled()) {
                            NAMING_LOGGER.debug("request {} failed.", nacosDomain, e);
                        }
                    }
                }
            }
    
            NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}",
                api, servers, exception.getErrCode(), exception.getErrMsg());
    
            throw new NacosException(exception.getErrCode(), "failed to req API:/api/" + api + " after all servers(" + servers + ") tried: "
                + exception.getMessage());
    
        }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值