Spring-Cloud-Starter-Alibaba-Nacos-Discovery 源码分析

目录

  • 0 版本
  • 1 Spring Boot
    • 1.1.@EnableDiscoveryClient , 启用服务发现
  • 2 Spring-Cloud-Commons
    • 2.1 @EnableDiscoveryClient: 通过注解导入 EnableDiscoveryClientImportSelector
      • 2.1.1 EnableDiscoveryClientImportSelector 启用服务发现客户端导入选择器
    • 2.2 AutoServiceRegistrationConfiguration 自动服务注册配置
      • 2.2.1 @EnableConfigurationProperties, 启用配置属性
      • 2.2.3 EnableConfigurationPropertiesRegistrar
      • 2.3.2 ConfigurationPropertiesBeanRegistrar
      • 2.2.2 AutoServiceRegistrationProperties
  • 3 Spring-Cloud-Starter-Alibaba-Nacos-Discovery
    • 3.1 NacosDiscoveryClientConfigServiceBootstrapConfiguration
    • 3.2 NacosDiscoveryAutoConfiguration
      • 3.2.1 NacosDiscoveryProperties
        • 3.2.1.1 NacosAutoServiceRegistration NacosDiscoveryInfoChangedEvent事件监听和处理
        • 3.2.1.2 AbstractAutoServiceRegistration
        • 3.2.1.3 Registration & ServiceInstance
    • 3.3 NacosServiceRegistryAutoConfiguration

0 版本

  • spring-cloud-starter-alibaba-nacos-discovery 2021.0.1.0
  • spring-cloud-commons 3.1.1
  • spring-boot-starter-web 2.6.3

1 Spring Boot

1.1.@EnableDiscoveryClient , 启用服务发现

通过在Aplication 处添加该注解, 启用服务发现:

@SpringBootApplication
@EnableDiscoveryClient // 启用服务发现
public class MsGatewayApplication {

这个注解 @EnableDiscoveryClient 是在Spring-Cloud-Commons里定义的

2 Spring-Cloud-Commons

2.1 @EnableDiscoveryClient: 通过注解导入 EnableDiscoveryClientImportSelector

查看该注解定义, 可以看到 @Import({EnableDiscoveryClientImportSelector.class}) ,这个注解会在启动时, 自动注入 EnableDiscoveryClientImportSelector


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableDiscoveryClientImportSelector.class}) // 通过注解导入 EnableDiscoveryClientImportSelector
public @interface EnableDiscoveryClient {
    boolean autoRegister() default true;
}

2.1.1 EnableDiscoveryClientImportSelector 启用服务发现客户端导入选择器

EnableDiscoveryClientImportSelector 的主要功能是导入需要的 AutoServiceRegistrationConfiguration (自动服务注册配置类) 或者 修改环境变量,这取决于 @EnableDiscoveryClient 里的 autoRegister 参数

根据 autoRegister(默认为true)判断:
 
如果为 true :     importsList.add(“org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration”) 是在引用列表里添加 org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration , 这个类是在 spring-coud-commons 项目里定义的
   
如果为 false :
map.put(“spring.cloud.service-registry.auto-registration.enabled”, false) 在环境变量中将 自动注册 设为 false
 

@Order(2147483547)
public class EnableDiscoveryClientImportSelector extends SpringFactoryImportSelector<EnableDiscoveryClient> {
    public EnableDiscoveryClientImportSelector() {
    }

    public String[] selectImports(AnnotationMetadata metadata) {
        String[] imports = super.selectImports(metadata);
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(this.getAnnotationClass().getName(), true));
        boolean autoRegister = attributes.getBoolean("autoRegister");
        // 是否自动注册服务 EnableDiscoveryClient.autoRegister default true
        if (autoRegister) {
            // 引用列表
	        List<String> importsList = new ArrayList(Arrays.asList(imports));
	        // 调用 spring-cloud-commons  里的 AutoServiceRegistrationConfiguration (自动服务注册配置)
            importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
            imports = (String[])importsList.toArray(new String[0]);
        } else {
            Environment env = this.getEnvironment();
            if (ConfigurableEnvironment.class.isInstance(env)) {
                ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
                LinkedHashMap<String, Object> map = new LinkedHashMap();
                // 配置 spring.cloud.service-registry.auto-registration.enabled 为false,不启用自动注册服务
                map.put("spring.cloud.service-registry.auto-registration.enabled", false);
                MapPropertySource propertySource = new MapPropertySource("springCloudDiscoveryClient", map);
	            // 添加到环境变量
                configEnv.getPropertySources().addLast(propertySource);
            }
        }

        return imports;
    }
  
    // 根据 配置项 spring.cloud.discovery.enabled 判断是否启用服务发现,该配置项默认为true
    protected boolean isEnabled() {
        // 从环境变量中获取 spring.cloud.discovery.enabled  的布尔值,并且默认返回TRUE
        return (Boolean)this.getEnvironment().getProperty("spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
    }

    protected boolean hasDefaultFactory() {
        return true;
    }
}

2.2 AutoServiceRegistrationConfiguration 自动服务注册配置

@EnableConfigurationProperties(AutoServiceRegistrationProperties.class) :
该注解的意思是启用配置类 AutoServiceRegistrationProperties
 
@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true):
spring.cloud.service-registry.auto-registration.enabled 的配置项为 true 的时候该类才会启用, 属性 matchIfMissing = true 的意思是假如没有对该项进行配置, 则默认情况下相当于 true,也会启用该类

/**
 * @author Spencer Gibb
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class) // 启用配置 AutoServiceRegistrationProperties
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)  // spring.cloud.service-registry.auto-registration.enabled 的配置项为true的时候,才会启用, matchIfMissing = true 为该配置项如果未配置,也会启用该类
public class AutoServiceRegistrationConfiguration {

}

2.2.1 @EnableConfigurationProperties, 启用配置属性

AutoServiceRegistrationConfiguration @Import(EnableConfigurationPropertiesRegistrar.class):
导入了 EnableConfigurationPropertiesRegistrar (一个关于启用属性配置的注册器),该注册器会将 @EnableConfigurationProperties 里的 Class[] value() 进行bean注册 (即对 AutoServiceRegistrationProperties 进行bean注册)

/**
 * Enable support for {@link ConfigurationProperties @ConfigurationProperties} annotated
 * beans. {@code @ConfigurationProperties} beans can be registered in the standard way
 * (for example using {@link Bean @Bean} methods) or, for convenience, can be specified
 * directly on this annotation.
 *
 * @author Dave Syer
 * @since 1.0.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
	
	// 验证器 BEAN 名称 
	/**
	 * The bean name of the configuration properties validator.
	 * @since 2.2.0
	 */
	String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

	// 接收一个数组,数组类型为ClassType
	/**
	 * Convenient way to quickly register
	 * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
	 * Spring. Standard Spring Beans will also be scanned regardless of this value.
	 * @return {@code @ConfigurationProperties} annotated beans to register
	 */
	Class<?>[] value() default {};

}

2.2.3 EnableConfigurationPropertiesRegistrar

AutoServiceRegistrationProperties 是怎么被引入呢
这类实现了接口 ImportBeanDefinitionRegistrar , 重写了registerBeanDefinitions 方法,它在这里将之前的 AutoServiceRegistrationProperties 通过 ConfigurationPropertiesBeanRegistrar 注册到容器

/**
 * {@link ImportBeanDefinitionRegistrar} for
 * {@link EnableConfigurationProperties @EnableConfigurationProperties}.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 */
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
 
	private static final String METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME = Conventions
			.getQualifiedAttributeName(EnableConfigurationPropertiesRegistrar.class, "methodValidationExcludeFilter");

	
	// 注册
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		registerInfrastructureBeans(registry);
		registerMethodValidationExcludeFilter(registry);
		// new 一个ConfigurationPropertiesBean注册器
		ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
	
		getTypes(metadata).forEach(beanRegistrar::register);
		/* 类似:依次注册 getTypes 返回的Class集 
		getTypes(metadata).forEach( (item) -> {
			beanRegistrar.register(item)
		} )
		*/
	}

	// 获取EnableConfigurationProperties集
	private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
		return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
				// 合并
				.flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
				// 过滤空的class
				.filter((type) -> void.class != type)
				// 返回set
				.collect(Collectors.toSet());
	}
	
	static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
		ConfigurationPropertiesBindingPostProcessor.register(registry);
		BoundConfigurationProperties.register(registry);
	}

	// 注册方法验证排除过滤器
	static void registerMethodValidationExcludeFilter(BeanDefinitionRegistry registry) {
		// 如果注册器不包含 METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME 的BeanDefinition
		if (!registry.containsBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME)) {
			BeanDefinition definition = BeanDefinitionBuilder
					// 生成一个BeanDefinition, 过滤 @ConfigurationProperties 的注解 
					.genericBeanDefinition(MethodValidationExcludeFilter.class,
							() -> MethodValidationExcludeFilter.byAnnotation(ConfigurationProperties.class))
					// 设置BeanDefinition的角色
					.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition();
			// 注册过滤器
			registry.registerBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME, definition);
		}
	}

}

2.3.2 ConfigurationPropertiesBeanRegistrar

该类是一个Bean注册器,在 void register 方法里:

	void register(Class<?> type) {
		MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
				.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
		register(type, annotation);
	}

	// 注册
	void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
		String name = getName(type, annotation);
		// 如果没有该BeanDefinition,则进行注册
		if (!containsBeanDefinition(name)) {
			registerBeanDefinition(name, type, annotation);
		}
	}

通过下面这行代码从 Class type 里 拿到 MergedAnnotation< ConfigurationProperties > annotation

MergedAnnotation<ConfigurationProperties> annotation =   MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class)

然后通过下面的断言:

Assert.state(annotation.isPresent(), () -> {
 	return "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on  '" + type.getName() + "'.";
}); 

来判断是否带有 ConfigurationProperties.class 类型的注解,接下来通过构造函数里传入的 BeanDefinitionRegistry registry ,把它注册到容器里:

private void registerBeanDefinition(String beanName, Class<?> type,
			MergedAnnotation<ConfigurationProperties> annotation) {
		Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
				+ " annotation found on  '" + type.getName() + "'.");
		this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
	}

它的属性 BeanDefinitionRegistry registry 是一个接口,具体传进来的实现类由容器进行管理, 这里只需要知道它实现了这组接口,调用它的 registerBeanDefinition() 方法进行bean注册就可以, ConfigurationPropertiesBeanRegistrar 的意义是它做了一个 过滤去重 ,过滤出 ConfigurationProperties 类型的注解, 通过 containsBeanDefinition() 方法判断之前是否已经注册过相同的Bean,在注册时对不同的 BindMethod( JAVA_BEAN, VALUE_OBJECT)Bean名称 进行了统一的处理。

它的构造函数这里做了一个强制转换, 把 BeanDefinitionRegistry 转换成 BeanFactory, 但是 BeanDefinitionRegistry 里并没有没有 BeanFactory 的约束,这应该是作者自己知道具体的实现类,然后做的转换。
 
ConfigurationPropertiesBeanRegistrar 的构造函数:

private final BeanDefinitionRegistry registry;
    private final BeanDefinitionRegistry registry;
    private final BeanFactory beanFactory;
    
    ConfigurationPropertiesBeanRegistrar(BeanDefinitionRegistry registry) {
        this.registry = registry;
        this.beanFactory = (BeanFactory)this.registry;
    }

统一获取Bean名称的方法:

	private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
		String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
		return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
	}

对不同类型的 BindMethod 处理:

 private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
        BindMethod bindMethod = BindMethod.forType(type);
        RootBeanDefinition definition = new RootBeanDefinition(type);
        definition.setAttribute(BindMethod.class.getName(), bindMethod);
        if (bindMethod == BindMethod.VALUE_OBJECT) {
            definition.setInstanceSupplier(() -> {
                return this.createValueObject(beanName, type);
            });
        }

        return definition;
    }

完整 ConfigurationPropertiesBeanRegistrar 的类代码:

final class ConfigurationPropertiesBeanRegistrar {

	private final BeanDefinitionRegistry registry;

	private final BeanFactory beanFactory;

	ConfigurationPropertiesBeanRegistrar(BeanDefinitionRegistry registry) {
		// 注入 bean注册器
		this.registry = registry;
		this.beanFactory = (BeanFactory) this.registry;
	}

	void register(Class<?> type) {
		MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
				.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
		register(type, annotation);
	}

	// 注册
	void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
		String name = getName(type, annotation);
		// 如果没有该BeanDefinition,则进行注册
		if (!containsBeanDefinition(name)) {
			registerBeanDefinition(name, type, annotation);
		}
	}

	// 获取名称:"prefix-typeName" , 或 "typeName"
	private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
		String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
		return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
	}

	private boolean containsBeanDefinition(String name) {
		return containsBeanDefinition(this.beanFactory, name);
	}

	// 判断是否包含BeanDefinition
	private boolean containsBeanDefinition(BeanFactory beanFactory, String name) {
		if (beanFactory instanceof ListableBeanFactory
				&& ((ListableBeanFactory) beanFactory).containsBeanDefinition(name)) {
			return true;
		}
		if (beanFactory instanceof HierarchicalBeanFactory) {
			return containsBeanDefinition(((HierarchicalBeanFactory) beanFactory).getParentBeanFactory(), name);
		}
		return false;
	}

	// 注册BeanDefinition
	private void registerBeanDefinition(String beanName, Class<?> type,
			MergedAnnotation<ConfigurationProperties> annotation) {
		Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
				+ " annotation found on  '" + type.getName() + "'.");
		this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
	}

	// 根据bean名称,类型,创建一个 BeanDefinition
	private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
		if (BindMethod.forType(type) == BindMethod.VALUE_OBJECT) {
			// 返回一个 ConfigurationProperties 值对象的BeanDefinition
			return new ConfigurationPropertiesValueObjectBeanDefinition(this.beanFactory, beanName, type);
		}
		GenericBeanDefinition definition = new GenericBeanDefinition();
		definition.setBeanClass(type);
		return definition;
	}
}

2.2.2 AutoServiceRegistrationProperties

这个类主要是加载一些配置,映射配置文件配置:

ConfigurationProperties("spring.cloud.service-registry.auto-registration")
public class AutoServiceRegistrationProperties {
    private boolean enabled = true;
    private boolean registerManagement = true;
    private boolean failFast = false;

    public AutoServiceRegistrationProperties() {
    }

    public boolean isEnabled() {
        return this.enabled;
    }
	...
	..

3 Spring-Cloud-Starter-Alibaba-Nacos-Discovery

3.1 NacosDiscoveryClientConfigServiceBootstrapConfiguration

在 SpringBootApplication 添加了 @EnabledDiscoveryClient 注解之后,什么时候启动服务呢

在 spring-cloud-starter-alibaba-nacos-discovery 的项目里的 spring.factories 中,定义了该启动配置NacosDiscoveryClientConfigServiceBootstrapConfiguration ,因此会在启动的时候加载该类。

Spring-Cloud-Starter-Alibaba-Nacos-Discovery 源码分析_第1张图片

查看 NacosDiscoveryClientConfigServiceBootstrapConfiguration 的定义,它是一个空的 class,主要是通过 @ImportAutoConfiguration 这个注解,在启动时导入了以下几个类 :

  • NacosDiscoveryAutoConfiguration.class,
  • NacosServiceAutoConfiguration.class,
  • NacosDiscoveryClientConfiguration.class,
  • NacosReactiveDiscoveryClientConfiguration.class

它们的后缀都是 Configuration,它还有一些其他的注解:

  @ConditionalOnClass({ConfigServicePropertySourceLocator.class}) 

上面这个注解是判断 ConfigServicePropertySourceLocator 这个class是否在classpath中, 在的话才会构建这个Bean

@ConditionalOnProperty(
    value = {"spring.cloud.config.discovery.enabled"},
    matchIfMissing = false
)

上面这个注解判断 spring.cloud.config.discovery.enabled 的配置是否为 true ,是的话才会构建这个Bean
它们都带有 ConditionalOn 的 前缀,都是起到 构建Bean条件判断的作用。
 
完整的类代码:

@ConditionalOnClass({ConfigServicePropertySourceLocator.class})
@ConditionalOnProperty(
    value = {"spring.cloud.config.discovery.enabled"},
    matchIfMissing = false
)
@Configuration(
    proxyBeanMethods = false
)
@ImportAutoConfiguration({NacosDiscoveryAutoConfiguration.class, NacosServiceAutoConfiguration.class, NacosDiscoveryClientConfiguration.class, NacosReactiveDiscoveryClientConfiguration.class})
public class NacosDiscoveryClientConfigServiceBootstrapConfiguration {
    public NacosDiscoveryClientConfigServiceBootstrapConfiguration() {
    }
}

3.2 NacosDiscoveryAutoConfiguration

@ConditionalOnDiscoveryEnabled 和 @ConditionalOnNacosDiscoveryEnabled 同样是构建Bean条件判断的注解 。
 
在类 NacosDiscoveryAutoConfiguration 里定义了 NacosDiscoveryProperties nacosProperties()cosServiceDiscovery nacosServiceDiscovery 两个方法, 通过@Bean 注解,定义 NacosDiscoveryPropertiesNacosServiceDiscovery 这两个Bean,在启动时,Spring容器会将这两个Bean注册到容器中。

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

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

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

3.2.1 NacosDiscoveryProperties

@ConfigurationProperties(“spring.cloud.nacos.discovery”) 会将这个前缀的配置映射到配置类的属性中。
 
它有一个带有 @PostConstruct 注解的 void init() 方法。
 
@PostConstruct 注解的方法会执行在构造函数之后,init() 函数之前
这里的方法同时命名为 init(),并且带有 @PostConstruct 注解, 因此在该类实例化后,会执行该方法。
 
它的 init() 方法里主要是初始化相关的配置,比如: serverAddr(服务地址),namespace(命名控件),username(用户名),password(密码)等。
 
通过 if (this.nacosServiceManager.isNacosDiscoveryInfoChanged(this)) 判断这一行代码判断配置是否修改了。如果修改了,则通过 this.applicationEventPublisher.publishEvent(new NacosDiscoveryInfoChangedEvent(this)) 发布 一个NacosDiscoveryInfoChangedEvent 类型的事件。
 
可以在 SringApplication.run() 执行之前, 通过
System.setProperty(“spring.cloud.nacos.config.server-addr”, “127.0.0.1:port”)
System.setProperty(“spring.cloud.nacos.discovery.server-addr”, “127.0.0.1:port”)
修改配置,它会在 this.overrideFromEnv(this.environment) 这里读取并覆盖。

// 映射配置
@ConfigurationProperties("spring.cloud.nacos.discovery")
public class NacosDiscoveryProperties {
    private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryProperties.class);
    public static final String PREFIX = "spring.cloud.nacos.discovery";
    private static final Pattern PATTERN = Pattern.compile("-(\\w)");
    private String serverAddr;
    private String username;
    private String password;
    private String endpoint;
    private String namespace;
    private long watchDelay = 30000L;

 	// 在构造函数之后,执行该方法
  @PostConstruct
   public void init() throws Exception {
        this.metadata.put("preserved.register.source", "SPRING_CLOUD");
        if (this.secure) {
            this.metadata.put("secure", "true");
        }
		// 获取配置的 nacos 服务地址
        this.serverAddr = Objects.toString(this.serverAddr, "");
        if (this.serverAddr.endsWith("/")) {
            this.serverAddr = this.serverAddr.substring(0, this.serverAddr.length() - 1);
        }

        this.endpoint = Objects.toString(this.endpoint, "");
        this.namespace = Objects.toString(this.namespace, "");
        this.logName = Objects.toString(this.logName, "");
        if (StringUtils.isEmpty(this.ip)) {
            if (StringUtils.isEmpty(this.networkInterface)) {
                this.ip = this.inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
            } else {
                NetworkInterface netInterface = NetworkInterface.getByName(this.networkInterface);
                if (null == netInterface) {
                    throw new IllegalArgumentException("no such interface " + this.networkInterface);
                }

                Enumeration inetAddress = netInterface.getInetAddresses();

                while(inetAddress.hasMoreElements()) {
                    InetAddress currentAddress = (InetAddress)inetAddress.nextElement();
                    if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) {
                        this.ip = currentAddress.getHostAddress();
                        break;
                    }
                }

                if (StringUtils.isEmpty(this.ip)) {
                    throw new RuntimeException("cannot find available ip from network interface " + this.networkInterface);
                }
            }
        }
	
		// 重写配置
        this.overrideFromEnv(this.environment);
		// 如果配置修改了,发布一个 NacosDiscoveryInfoChangedEvent 事件
        if (this.nacosServiceManager.isNacosDiscoveryInfoChanged(this)) {
            this.applicationEventPublisher.publishEvent(new NacosDiscoveryInfoChangedEvent(this));
        }
    }

3.2.1.1 NacosAutoServiceRegistration NacosDiscoveryInfoChangedEvent事件监听和处理

在 NacosDiscoveryProperties 初始化之后,执行 @PostConstruct 注解的 init() 方法, 发布了一个 NacosDiscoveryInfoChangedEvent 类型的事件。
这个事件是在哪里被监听和处理的呢?
 
通过全局搜索,找到了 NacosAutoServiceRegistration 这个类,它有一个 void onNacosDiscoveryInfoChangedEvent 的方法,这个方法用一个
@EventListener 的注解修饰, 并且接收了一个 NacosDiscoveryInfoChangedEvent 类型的参数,这个刚好和之前发布的事件的类型是一样的:

/**
 * @author xiaojing
 * @author Mercy
 */
public class NacosAutoServiceRegistration
		extends AbstractAutoServiceRegistration<Registration> {

	...
	... 
	// 事件监听:NacosDiscoveryInfoChangedEvent
	@EventListener
	public void onNacosDiscoveryInfoChangedEvent(NacosDiscoveryInfoChangedEvent event) {
		// 重启
		restart();
	}
	
	private void restart() {
		this.stop();
		this.start();
	}

	...

它起到监听 NacosDiscoveryInfoChangedEvent 事件并且对事件进行处理的这样一个作用。通过 @EventListener 注解, SpringBoot初始化时会注册一个监听和处理该事件的 Listener, 通过 applicationEventPublisher.publishEvent() 发布一个事件的时候, 会根据事件的类型,在已经注册的 Listener 里找到对应的处理者,可能一个事件同时会有多个Listener,它会把发布的事件广播给所有的 Listener, 每个 Listener 对事件进行各自的处理。
 
onNacosDiscoveryInfoChangedEvent() 这里执行的是 restart() 重启,其实是执行了继承的 stop()start() 方法

该类继承自在SpringCloud中定义的泛型抽象类
AbstractAutoServiceRegistration < T extends Registration >
 
实现了 AutoServiceRegistration, ApplicationContextAware , ApplicationListener < WebServerInitializedEvent > 三个接口
 
ApplicationContextAware 接口中定义了 void setApplicationContext(ApplicationContext applicationContext)
 

ApplicationListener < WebServerInitializedEvent > 定义了 void onApplicationEvent(WebServerInitializedEvent event)

NacosAutoServiceRegistration 的类关系简图

Spring-Cloud-Starter-Alibaba-Nacos-Discovery 源码分析_第2张图片

ApplicationContextAware 接口中定义了 void setApplicationContext(ApplicationContext applicationContext) ,这里用了接口的方式,调用者知道你实现了这个接口规范 。通过这个方法可以在某个时候(比如初始化时),把 ApplicationContext 传过来

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

3.2.1.2 AbstractAutoServiceRegistration

void onApplicationEvent 方法中,当 WebServerInitializedEvent 事件传进来后, 执行 bind() 方法。
 
bind() 方法中, 主要做一些逻辑判断,判断事件里的应用程序上下文,和命名空间。

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

这里的 compareAndSet 的用法是,当 this.port 的值为0的时候,将 this.port 的值设置为 event.getWebServer().getPort() , 即事件里的端口。
然后执行 this.start()

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

start() 方法里主要是做一些状态判断:isEnabled() 是否启用, running 是否运行中等。
 
然后在执行 this.register() 之前, 发布一个 InstancePreRegisteredEvent 类型的事件, 并且在之后发布一个 InstanceRegisteredEvent 类型的事件。
 
这样我们只要知道一个类继承自 AbstractAutoServiceRegistration , 调用它的 start() 方法,并且成功注册的话,它就会在注册前后发布这两个事件,需要的情况下可以定义两个事件监听器,去捕获和处理这两个事件。

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

        } else {
            if (!this.running.get()) {
                this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
                this.register();
                if (this.shouldRegisterManagement()) {
                    this.registerManagement();
                }

                this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
                this.running.compareAndSet(false, true);
            }

        }
    }

register() 方法里, 调用的 this.serviceRegistry 是在构造函数里传入的一个 ServiceRegistry < R extends Registration > 类型的接口。
 
getRegistration() 这个方法在抽象类里并没有具体实现,只是作了定义,该方法返回泛型 R( 泛型约束:继承自 Registration ):


	protected abstract R getRegistration();
	
	protected void register() {
        this.serviceRegistry.register(this.getRegistration());
    }
    
	private final ServiceRegistry<R> serviceRegistry;
	
 	@Deprecated
    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
        this.serviceRegistry = serviceRegistry;
        this.properties = properties;
    }

再看 stop() 方法这部分, 同样是对状态作判断,并且调用的是 serviceRegistryderegister() 方法 和 close() 方法。
 
serviceRegistryServiceRegistry< R extends Registration> 类型的接口。任何具体的该接口的实现类,都可以通过在构造函数中以参数的方式注入, 被用来做服务状态具体的操作。

 	public void stop() {
        if (this.getRunning().compareAndSet(true, false) && this.isEnabled()) {
            this.deregister();
            if (this.shouldRegisterManagement()) {
                this.deregisterManagement();
            }

            this.serviceRegistry.close();
        }

    }

ServiceRegistry< R extends Registration> 的接口定义,该接口定义了一个服务注册类应该实现的几个方法:

public interface ServiceRegistry<R extends Registration> {

	/**
	 * Registers the registration. A registration typically has information about an
	 * instance, such as its hostname and port.
	 * @param registration registration meta data
	 */
	void register(R registration);

	/**
	 * Deregisters the registration.
	 * @param registration registration meta data
	 */
	void deregister(R registration);

	/**
	 * Closes the ServiceRegistry. This is a lifecycle method.
	 */
	void close();

	/**
	 * Sets the status of the registration. The status values are determined by the
	 * individual implementations.
	 * @param registration The registration to update.
	 * @param status The status to set.
	 * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
	 */
	void setStatus(R registration, String status);

	/**
	 * Gets the status of a particular registration.
	 * @param registration The registration to query.
	 * @param  The type of the status.
	 * @return The status of the registration.
	 * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
	 */
	<T> T getStatus(R registration);

}

3.2.1.3 Registration & ServiceInstance

在看看泛型R的约束类 Registration , 查看定义后发现它是空的,他只是对 ServiceInstance 的一层封装, 为什么这么多此一举呢,我想应该是为了在抽象概念上达到更清晰的一个目的。
 
ServiceInstance 同样也是在 SpringCloud 中定义的一组接口,它定义的方法:

方法 描述
String getHost() 获取主机名称
int getPort() 获取端口
boolean isSecure() 是否安全
URI getUri() 获取服务的Uri地址
Map getMetadata() 获取服务实例的元数据
String getInstanceId() 获取唯一的实例Id
String getServiceId() 获取服务Id
String getScheme() 获取方案信息

按照SpringCloud文档里的说明,每个注册的服务实例都会有唯一的 InstanceId, 而多个实例可能有同一个 ServiceId (如 “service-order” )

public interface Registration extends ServiceInstance {
} 
public interface ServiceInstance {
    default String getInstanceId() {
        return null;
    }

    String getServiceId();
	
    String getHost();

    int getPort();

    boolean isSecure();

    URI getUri();

    Map<String, String> getMetadata();

    default String getScheme() {
        return null;
    }
}

再回到 NacosAutoServiceRegistration , 它有个 NacosRegistration registration 的属性, 构造函数的参数个数也增加了一个 NacosRegistration registration,这个类实现了SpringCloud的 Registration 接口。
 
它同时 实现了 Registration, ServiceInstance , 两个接口, 不过既然 Registration 是空的,且 Registration 继承 ServiceInstance ,那这两者方法应该没有太大区别,有的只是概念上的差异,我们依然可以理解成它是一个 Registration

public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {
    
    private NacosRegistration registration;

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

可以看到 NacosRegistration 的构造函数有3个参数, 关于nacos的操作主要是在 NacosDiscoveryProperties 里进行的

 public NacosRegistration(List<NacosRegistrationCustomizer> registrationCustomizers, NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        this.registrationCustomizers = registrationCustomizers;
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
        this.context = context;
    }

那这个 NacosAutoServiceRegistration 是在什么时候被实例化的呢?现在又回到之前看过的 spring.factories, 在这里了找到一个 com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration 的配置类 , 同时@Enabled,该类会在启动的时候自动加载:

Spring-Cloud-Starter-Alibaba-Nacos-Discovery 源码分析_第3张图片

3.3 NacosServiceRegistryAutoConfiguration

找到 NacosServiceRegistryAutoConfiguration 这个类的文件,查看它的定义, 它有3个用@Bean注解的方法 :

  • NacosServiceRegistry nacosServiceRegistry()
  • NacosRegistration nacosRegistration()
  • NacosAutoServiceRegistration nacosAutoServiceRegistration()

@AutoConfigureAfter 注解的作用下,这个类将会在 这两个配置加载完之后再加载:

  • AutoServiceRegistrationConfiguration.class
  • AutoServiceRegistrationAutoConfiguration.class

由@Bean注解的方法 NacosAutoServiceRegistration nacosAutoServiceRegistration() 方法,就是要找的 NacosAutoServiceRegistration Bean被注册的地方 , 它会在配置类加载的时候, 被注册到容器中。
 
nacosAutoServiceRegistration 依赖的 NacosServiceRegistryNacosRegistration 在这个配置类里都有定义,它们也是通过@Bean修饰的方法,同样会被注册到容器中,通过注入的方式注入到参数里。
 
而另一个参数类型 AutoServiceRegistrationProperties 的命名空间是在 SpringCloud Commons 项目里,因此要到那里去找这个Bean被注册的地方。

/**
 * @author xiaojing
 * @author Mercy
 */
@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 {

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

	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosRegistration nacosRegistration(
			ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
			NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
		return new NacosRegistration(registrationCustomizers.getIfAvailable(),
				nacosDiscoveryProperties, context);
	}

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

}

可以看到是在 SpringCloud Common里被加载的,通过 AutoServiceRegistrationConfiguration 的 @EnableConfigurationProperties(AutoServiceRegistrationProperties.class) 注解:

/**
 * @author Spencer Gibb
 */
@Configuration(proxyBeanMethods = false)
// 通过注解 @EnableConfigurationProperties 启用指定的 AutoServiceRegistrationProperties.class 配置
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
// 根据 spring.cloud.service-registry.auto-registration.enabled 判断是否启用该类, matchIfMissing = true 默认为true
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {

}

AutoServiceRegistrationConfiguration 又是通过 AutoServiceRegistrationAutoConfiguration 类里的
@Import(AutoServiceRegistrationConfiguration.class) 被导入,也就是说 AutoServiceRegistrationAutoConfiguration 加载的时候,通过该注解会同时加载 AutoServiceRegistrationConfiguration 这个类, AutoServiceRegistrationAutoConfiguration 是什么时候被加载的呢?

/**
 * @author Spencer Gibb
 */
@Configuration(proxyBeanMethods = false)
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration implements InitializingBean {

	@Autowired(required = false)
	private AutoServiceRegistration autoServiceRegistration;

	@Autowired
	private AutoServiceRegistrationProperties properties;
 	
 	...

AutoServiceRegistrationAutoConfiguration 是被写在 SpringCloud Commons 项目里的 spring.factories 的自动配置项里 , 同时 @EnableDiscoveryClient 注解里也把这个类加入到 ImportList 里面 。因此它会在启动时被自动加载,这样 AutoServiceRegistrationConfiguration 也会被注册到容器中。

Spring-Cloud-Starter-Alibaba-Nacos-Discovery 源码分析_第4张图片

以上是关于 Spring-Cloud-Starter-Alibaba-Nacos-Discovery 在应用启动时,自动注册服务的一些相关的源码分析。在应用程序开始运行后, 加载Bean NacosDiscoveryProperties 时,在它构造函数执行完的时候,会自动调用 init() 方法,完成服务注册的操作。

你可能感兴趣的:(微服务,java,spring,cloud,服务发现,微服务)