面试宝典-Nacos源码解析(二)

546 阅读4分钟

Nacos服务注册的实现

Nacos的服务注册主要是实现了NacosAutoServiceRegistration类、NacosRegistration类与NacosServiceRegistry类

NacosRegistration

首先看看NacosRegistration,这个类管理了Nacos服务的基本信息,比如服务名、服务地址等等。实际上Nacos服务的基本信息都是由NacosDiscoveryProperties这个类来保存的,由NacosRegistration进行封装信息。

public class NacosRegistration implements Registration, ServiceInstance {

	/**
	 * management port
	 */
	public static final String MANAGEMENT_PORT = "management.port";

	/**
	 * management context-path.
	 */
	public static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";

	/**
	 * management address.
	 */
	public static final String MANAGEMENT_ADDRESS = "management.address";

	/**
	 * management endpoints web base path.
	 */
	public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path";

    // 配置文件中spring.cloud.nacos.discovery开头的配置的映射类
	private NacosDiscoveryProperties nacosDiscoveryProperties;
    
    // 应用上下文
	private ApplicationContext context;

    // 构造函数注入
	public NacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
		this.nacosDiscoveryProperties = nacosDiscoveryProperties;
		this.context = context;
	}

    // 当构造函数执行之后执行的初始化
	@PostConstruct
	public void init() {
        
		Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
		Environment env = context.getEnvironment();

		String endpointBasePath = env.getProperty(MANAGEMENT_ENDPOINT_BASE_PATH);
		if (!StringUtils.isEmpty(endpointBasePath)) {
			metadata.put(MANAGEMENT_ENDPOINT_BASE_PATH, endpointBasePath);
		}

		Integer managementPort = ManagementServerPortUtils.getPort(context);
		if (null != managementPort) {
			metadata.put(MANAGEMENT_PORT, managementPort.toString());
			String contextPath = env
					.getProperty("management.server.servlet.context-path");
			String address = env.getProperty("management.server.address");
			if (!StringUtils.isEmpty(contextPath)) {
				metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);
			}
			if (!StringUtils.isEmpty(address)) {
				metadata.put(MANAGEMENT_ADDRESS, address);
			}
		}

		if (null != nacosDiscoveryProperties.getHeartBeatInterval()) {
			metadata.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
					nacosDiscoveryProperties.getHeartBeatInterval().toString());
		}
		if (null != nacosDiscoveryProperties.getHeartBeatTimeout()) {
			metadata.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
					nacosDiscoveryProperties.getHeartBeatTimeout().toString());
		}
		if (null != nacosDiscoveryProperties.getIpDeleteTimeout()) {
			metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
					nacosDiscoveryProperties.getIpDeleteTimeout().toString());
		}
	}

	// getter、setter
    ...
}

NacosServiceRegistry

NacosServiceRegistry实现了ServiceRegistry接口,它主要是把服务实例注册到Nacos的注册中心。然后重点关注register与deregister,这两个方法主要是实现了服务的注册和反注册。

最后面Nacos都是通过NamingService来完成这两个功能的,NamingService在启动的时候封装了Nacos注册中心的信息,并且也把服务注册和反注册相关的http请求也封装了。

	@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();
        // 分组
		String group = nacosDiscoveryProperties.getGroup();
        // 创建服务实例对象
		Instance instance = getNacosInstanceFromRegistration(registration);

		try {
            // 通过namingService将服务实例注册到注册中心
			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);
		}
	}

	@Override
	public void deregister(Registration registration) {

		log.info("De-registering from Nacos Server now...");

		if (StringUtils.isEmpty(registration.getServiceId())) {
			log.warn("No dom to de-register for nacos client...");
			return;
		}
        // 获取namingService
		NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
		// 服务名
        String serviceId = registration.getServiceId();
        // 分组
		String group = nacosDiscoveryProperties.getGroup();

		try {
            // 通过namingService反注册当前服务实例
			namingService.deregisterInstance(serviceId, group, registration.getHost(),
					registration.getPort(), nacosDiscoveryProperties.getClusterName());
		}
		catch (Exception e) {
			log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",
					registration.toString(), e);
		}

		log.info("De-registration finished.");
	}

NacosAutoServiceRegistration

NacosAutoServiceRegistration这个类实现了服务自动注册到Nacos注册中心的功能

public abstract class AbstractAutoServiceRegistration<R extends Registration>
		implements AutoServiceRegistration, ApplicationContextAware,
		ApplicationListener<WebServerInitializedEvent> {
            
            ...
        }

可以看到,实现了ApplicationListener,说明可以被事件回调。当容器启动之后,应用上下文被刷新并且WebServer准备就绪之后,会触发WebServerInitializedEvent事件,那么抽象类中的onApplicationEvent(WebServerInitializedEvent event)方法就会被调用。

    @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();
		String group = nacosDiscoveryProperties.getGroup();

		Instance instance = getNacosInstanceFromRegistration(registration);

		try {
            // 最后由namingService实现服务注册
			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);
		}
	}

到最后面,实际调用的是serviceRegistry的register方法,而serviceRegistry是在构造NacosAutoServiceRegistration的时候传入的NacosServiceRegistry。所以实际上是调用的NacosServiceRegistry的register方法。

其他配置类NacosServiceRegistryAutoConfiguration

这个NacosServiceRegistryAutoConfiguration类是为了使用Spring Boot自动装配功能的一个自动配置类

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

}

第一步这个自动装配类首先使用了@EnableConfigurationProperties注解,这个注解能够使用@ConfigurationProperties注解但是的话却没有用@Component等注解实例化为bean的类生效。因为NacosDiscoveryProperties类使用了@ConfigurationProperties("spring.cloud.nacos.discovery")注解,但是没有@Component注解,Spring容器不会实例化这个类,所以需要通过@EnableConfigurationProperties注解让NacosDiscoveryProperties被实例化并注入到容器中。这样就能获取到配置文件中配置的Nacos相关的配置值。

第二步然后使用了@ConditionalOnNacosDiscoveryEnabled注解。这个注解的意思是spring.cloud.nacos.discovery.enabled=true的时候(默认是true),NacosServiceRegistryAutoConfiguration这个配置类才会生效。



第三步然后使用了@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)注解。这个注解的意思是当spring.cloud.service-registry.auto-registration.enabled=true的时候,NacosServiceRegistryAutoConfiguration这个配置类才会生效。默认情况下就是true。

第四步就使用注解,这个注解的意思是要在AutoServiceRegistrationConfiguration、AutoServiceRegistrationAutoConfiguration、NacosDiscoveryAutoConfiguration配置类生效后再使NacosServiceRegistryAutoConfiguration这个配置类生效。

@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class,
		NacosDiscoveryAutoConfiguration.class })

第五步查看NacosServiceRegistryAutoConfiguration配置类的内容,可以看出,这个配置类实例化了NacosServiceRegistry、NacosRegistration、NacosAutoServiceRegistration这三个bean。其中,NacosServiceRegistry负责服务注册和反注册,NacosRegistration负责维护服务实例的基本信息,NacosAutoServiceRegistration主要是实现服务的自动注册。