Nacos源码学习系列第4篇服务什么时候开始注册

344 阅读3分钟

目录

加载自动装配类

服务注册触发时机

服务注册核心流程

 核心代码讲解

总结


加载自动装配类

spring boot 在启动后扫描【spring-cloud-alibaba-nacos-discovery】jar包 下的spring.factory文件。

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

其中负责服务注册的自动化配置类是 org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration

该类会创建NacosServiceRegistry NacosRegistration NacosAutoServiceRegistration 3个Bean 

 @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosDiscoveryProperties);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration(nacosDiscoveryProperties, context);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }

NacosRegistration:封装服务注册的基本信息 包括服务的serviceId 服务的Ip 和 port 、Cluster(所属集群) 、Weight(服务的权重)、isSecure、uri(http[https]://ip:port/)、metaData(服务扩展数据)、namingService 这些属性基本上都是通过属性配置文件配置的 

NacosAutoServiceRegistration : 服务启动后该类监听web server 初始化事件触发 服务注册流程

NacosServiceRegistry 负责服务的注册和下线功能

服务注册触发时机

NacosAutoServiceRegistration 的父类AbstractAutoServiceRegistration监听了WebServer 初始化事件,当监听到web server 初始化事件时执行 bind(WebServerInitializedEvent event) 方法

服务启动事件触发方法:bind(WebServerInitializedEvent event):

//服务器在初始化时唤醒该方法
public void bind(WebServerInitializedEvent event) {
		ApplicationContext context = event.getApplicationContext();
        ...

		this.port.compareAndSet(0, event.getWebServer().getPort());

		this.start();
	}


public void start() {

		// 如果服务如果已注册过则跳过
		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);
		}

	}

// 调用NacosServiceRegistry 注册
protected void register() {
		this.serviceRegistry.register(getRegistration());
	}

通过代码可以看出服务注册是调用的serviceRegistry.registry方法.

这里的serviceRegistry 就是我们上面自动装配类里面的NacosServiceRegistry类

服务注册核心流程

 核心代码讲解

//服务启动后该类监听web server 初始化事件触发 服务注册流程
public class NacosAutoServiceRegistration
		extends AbstractAutoServiceRegistration<Registration> {

    //包装了服务注册基本信息[注册信息]   
	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 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 Object getConfiguration() {
		return this.registration.getNacosDiscoveryProperties();
	}

    //是否可以启用服务注册[纯消费者可以设置该属性为false]
	@Override
	protected boolean isEnabled() {
		return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
	}
    
    //首先从[spring.cloud.nacos.discovery.service]获取appName, 如果没有获取到则从 
    //        [spring.application.name]属性获取  
	@Override
	@SuppressWarnings("deprecation")
	protected String getAppName() {
		String appName = registration.getNacosDiscoveryProperties().getService();
		return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
	}

}
//实现ServiceRegistry接口的服务注册 spring正是通过该接口为nacos的注册服务提供了可能
public class NacosServiceRegistry implements ServiceRegistry<Registration> {

    ...
	private final NacosDiscoveryProperties nacosDiscoveryProperties;

	private final NamingService namingService;
    
    ...

	@Override
	public void register(Registration registration) {

		if (StringUtils.isEmpty(registration.getServiceId())) {
			log.warn("No service to register for nacos client...");
			return;
		}

		String serviceId = registration.getServiceId();
        //创建要注册的实例
		Instance instance = new Instance();
		instance.setIp(registration.getHost());
		instance.setPort(registration.getPort());
		instance.setWeight(nacosDiscoveryProperties.getWeight());
		instance.setClusterName(nacosDiscoveryProperties.getClusterName());
		instance.setMetadata(registration.getMetadata());

		...
			namingService.registerInstance(serviceId, instance);
			log.info("nacos registry, {} {}:{} register finished", serviceId,
					instance.getIp(), instance.getPort());
		...
	}

    //服务下线功能
	@Override
	public void deregister(Registration registration) {

		...

		NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
		String serviceId = registration.getServiceId();

		...
			namingService.deregisterInstance(serviceId, registration.getHost(),
					registration.getPort(), nacosDiscoveryProperties.getClusterName());
		...
	}

	@Override
	public void close() {

	}

	@Override
	public void setStatus(Registration registration, String status) {
		// nacos doesn't support set status of a particular registration.
	}

	@Override
	public <T> T getStatus(Registration registration) {
		// nacos doesn't support query status of a particular registration.
		return null;
	}

}

上面的serverId 就是属性文件配置的service 或者 appName 属性。

通过代码可以看出首先通过 Registration和  nacosDiscoveryProperties[配置文件相关属性] 构建一个Instance 对象 然后调用下一章要详细讲解的 NacosNamingService 统一发起注册和下线功能

注意: NacosRegistration 负责包装 配置属性文件里面的属性 。比spring cloud的 Registration  接口提供了更多的属性比如 registerWeight、Cluster、isRegisterEnabled等。因此你可以理解了 为什么 weight 性和  clusterName 要从 nacosDiscoveryProperties 去获而不是通过 registration 去获取 

instance.setWeight(nacosDiscoveryProperties.getWeight());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());

Instance: 注册的实例信息 字段如下类图:

  • Instance: 有几个重要的属性分别是: ip、端口、权重(不能小于1)、集群名、扩展信息    
  • nacos: 的数据模型从上到下 namespace -> service -> group -> cluster -> instance        
  • namespace : 待注册实例所属的空间一般可以表示不同的环境 默认是PUBLIC       
  • service: 注册服务名                                                                                                          
  • group: 待注册实例所属分组 默认是:DEFAULT_GROUP                                             
  • cluster: 待注册实例所属集群 默认是:DEFAULT

总结

 Nacos 之所以可以作为独立的第三方注册中心集成到spring cloud 里面核心的原因是spring cloud提供下面3个接口或抽象类作为扩展点

class AbstractAutoServiceRegistration

Interface ServiceRegistry

Interface Registration

所以nacos的第一个启动的自动装配类【NacosDiscoveryAutoConfiguration】就是 创建基于上面3个接口的实现类。

后面的所有注册流程就完全进入到了nacos自己的实现中。