SpringCloud之Consul源码解析

Consul初始化过程中涉及三个核心包,分别为spring-cloud-consul-core、spring-cloud-consul-config、spring-cloud-consul-discovery。
spring-cloud-consul-config包涉及两个核心类ConsulConfigAutoConfiguration、ConsulConfigBootstrapConfiguration。
spring-cloud-consul-core包涉及核心类ConsulAutoConfiguration。
spring-cloud-consul-discovery包涉及核心类如下:ConsulServiceRegistryAutoConfiguration、ConsulAutoServiceRegistrationAutoConfiguration等。

涉及的配置类信息:

  1. #2 ConsulAutoConfiguration#ConsulProperties:spring.cloud.consul。
  2. #1.1 ConsulConfigAutoConfiguration#ConsulConfigProperties:spring.cloud.consul.config。
  3. #3.1 ConsulServiceRegistryAutoConfiguration#ConsulDiscoveryProperties:spring.cloud.consul.discovery。
  4. #3.1 ConsulServiceRegistryAutoConfiguration#HeartbeatProperties:spring.cloud.consul.discovery.heartbeat。
  5. #3.2 ConsulAutoServiceRegistrationAutoConfiguration#AutoServiceRegistrationProperties:spring.cloud.service-registry.auto-registration。

1、核心包config涉及的核心类

该包下核心类ConsulConfigAutoConfiguration主要完成ConfigWatch类初始化。

1.1、ConsulConfigAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)
public class ConsulConfigAutoConfiguration {
    
    

	public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RefreshEndpoint.class)
	protected static class ConsulRefreshConfiguration {
    
    

		@Bean
		@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
				matchIfMissing = true)
		public ConfigWatch configWatch(ConsulConfigProperties properties,
				ConsulPropertySourceLocator locator, ConsulClient consul,
				@Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
    
    
			return new ConfigWatch(properties, consul, locator.getContextIndexes(),
					taskScheduler);
		}

		@Bean(name = CONFIG_WATCH_TASK_SCHEDULER_NAME)
		@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
				matchIfMissing = true)
		public TaskScheduler configWatchTaskScheduler() {
    
    
			return new ThreadPoolTaskScheduler();
		}
	}
}

ConsulClient是通过配置类ConsulAutoConfiguration实例化完毕的。
ConsulPropertySourceLocator是通过配置类ConsulConfigBootstrapConfiguration实例化完成的。

ConsulConfigProperties中两个核心配置属性:

  1. spring.cloud.consul.config.watch.delay=1000ms。
  2. spring.cloud.consul.config.watch.waitTime = 55s。

1.2、ConfigWatch

该类的主要作用是通过定时任务更新配置(KV)

public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {
    
    
	@Override 
	public void start() {
    
    // Lifecycle的start方法
		if (this.running.compareAndSet(false, true)) {
    
    
			//taskScheduler:ThreadPoolTaskScheduler
			this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
					this::watchConfigKeyValues, this.properties.getWatch().getDelay());
		}
	}
	
	public void watchConfigKeyValues() {
    
    
		if (this.running.get()) {
    
    
			for (String context : this.consulIndexes.keySet()) {
    
    
				Response<List<GetValue>> response = this.consul.getKVValues(context,aclToken,
							new QueryParams(this.properties.getWatch().getWaitTime(),currentIndex));
			}
		}
	}
}

ThreadPoolTaskScheduler相关功能参考

  1. 更新配置地址为:http://localhost:8500/v1/kv/config/custom-consul/。
  2. 该定时任务是通过SmartLifecycle生命周期控制触发的。

2、核心包core涉及的核心类ConsulAutoConfiguration

public class ConsulAutoConfiguration {
    
    
	@Bean
	@ConditionalOnMissingBean
	public ConsulProperties consulProperties() {
    
    
		return new ConsulProperties();
	}
	
	@Bean
	@ConditionalOnMissingBean
	public ConsulClient consulClient(ConsulProperties consulProperties) {
    
    
		final int agentPort = consulProperties.getPort();
		final String agentHost = !StringUtils.isEmpty(consulProperties.getScheme())
				? consulProperties.getScheme() + "://" + consulProperties.getHost()
				: consulProperties.getHost();
	
		if (consulProperties.getTls() != null) {
    
    
			ConsulProperties.TLSConfig tls = consulProperties.getTls();
			TLSConfig tlsConfig = new TLSConfig(tls.getKeyStoreInstanceType(),
					tls.getCertificatePath(), tls.getCertificatePassword(),
					tls.getKeyStorePath(), tls.getKeyStorePassword());
			return new ConsulClient(agentHost, agentPort, tlsConfig);
		}
		return new ConsulClient(agentHost, agentPort);
	}
}
  1. 主要初始化用于服务注册的客户端ConsulClient。
  2. 涉及的配置类为ConsulProperties
  3. ConsulClient通过http注册 服务端服务 于注册中心其请求的默认端口号为8500,可以通过ConsulProperties修改consul客户端接口的相关属性。

3、核心包discovery的核心类

3.1、ConsulServiceRegistryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.enabled",
		matchIfMissing = true)
@AutoConfigureBefore(ServiceRegistryAutoConfiguration.class)
public class ConsulServiceRegistryAutoConfiguration {
    
    

	@Bean
	@ConditionalOnMissingBean
	public ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient,
			ConsulDiscoveryProperties properties, HeartbeatProperties heartbeatProperties,
			@Autowired(required = false) TtlScheduler ttlScheduler) {
    
    
		return new ConsulServiceRegistry(consulClient, properties, ttlScheduler,
				heartbeatProperties);
	}

	@Bean
	@ConditionalOnMissingBean
	public HeartbeatProperties heartbeatProperties() {
    
    
		return new HeartbeatProperties();
	}

	@Bean
	@ConditionalOnMissingBean
	// TODO: Split appropriate values to service-registry for Edgware
	public ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {
    
    
		return new ConsulDiscoveryProperties(inetUtils);
	}

}
  1. 初始化ConsulServiceRegistry,用于ConsulAutoServiceRegistrationAutoConfiguration#ConsulAutoServiceRegistration。
  2. 配置类ConsulDiscoveryProperties。

3.2、ConsulAutoServiceRegistrationAutoConfiguration

服务注册的核心类。该类的核心目的是初始化事件监听器ConsulAutoServiceRegistrationListener。#5

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnMissingBean(
		type = "org.springframework.cloud.consul.discovery.ConsulLifecycle")
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
		matchIfMissing = true)
@AutoConfigureAfter({
    
     AutoServiceRegistrationConfiguration.class,
		ConsulServiceRegistryAutoConfiguration.class })
public class ConsulAutoServiceRegistrationAutoConfiguration {
    
    

	@Autowired
	AutoServiceRegistrationProperties autoServiceRegistrationProperties;

	@Bean
	@ConditionalOnMissingBean
	public ConsulAutoServiceRegistration consulAutoServiceRegistration(
			ConsulServiceRegistry registry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			ConsulDiscoveryProperties properties,
			ConsulAutoRegistration consulRegistration) {
    
    
		return new ConsulAutoServiceRegistration(registry,
				autoServiceRegistrationProperties, properties, consulRegistration);
	}
	// 真正触发注册功能。
	//监听器构造器构造参数:ConsulAutoServiceRegistration
	//ConsulAutoServiceRegistration构造器构造参数包含ConsulAutoRegistration实例化
	@Bean
	public ConsulAutoServiceRegistrationListener consulAutoServiceRegistrationListener(
			ConsulAutoServiceRegistration registration) {
    
    
		return new ConsulAutoServiceRegistrationListener(registration);
	}
	
	@Bean
	@ConditionalOnMissingBean
	public ConsulAutoRegistration consulRegistration(
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			ConsulDiscoveryProperties properties, ApplicationContext applicationContext,
			ObjectProvider<List<ConsulRegistrationCustomizer>> registrationCustomizers,
			ObjectProvider<List<ConsulManagementRegistrationCustomizer>> managementRegistrationCustomizers,
			HeartbeatProperties heartbeatProperties) {
    
    
		return ConsulAutoRegistration.registration(autoServiceRegistrationProperties,
				properties, applicationContext, registrationCustomizers.getIfAvailable(),
				managementRegistrationCustomizers.getIfAvailable(), heartbeatProperties);
	}
	...
}

3.2.1、ConsulAutoRegistration

public class ConsulAutoRegistration extends ConsulRegistration {
    
    

	public static ConsulAutoRegistration registration(
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			ConsulDiscoveryProperties properties, ApplicationContext context,
			List<ConsulRegistrationCustomizer> registrationCustomizers,
			List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers,
			HeartbeatProperties heartbeatProperties) {
    
    

		NewService service = new NewService();// NewService是consul中对于应用服务实例的抽象
		String appName = getAppName(properties, context.getEnvironment());//优先获取properties中serviceName,其次spring.application.name,再次application
		service.setId(getInstanceId(properties, context));
		if (!properties.isPreferAgentAddress()) {
    
    
			service.setAddress(properties.getHostname());
		}
		service.setName(normalizeForDns(appName));
		service.setTags(createTags(properties));

		if (properties.getPort() != null) {
    
    
			service.setPort(properties.getPort());
			// #5
			setCheck(service, autoServiceRegistrationProperties, properties, context,
					heartbeatProperties);
		}

		ConsulAutoRegistration registration = new ConsulAutoRegistration(service,
				autoServiceRegistrationProperties, properties, context,
				heartbeatProperties, managementRegistrationCustomizers);
		customize(registrationCustomizers, registration);
		return registration;
	}

	public static ConsulAutoRegistration managementRegistration(
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			ConsulDiscoveryProperties properties, ApplicationContext context,
			List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers,
			HeartbeatProperties heartbeatProperties) {
    
    
		NewService management = new NewService();
		management.setId(getManagementServiceId(properties, context));
		management.setAddress(properties.getHostname());
		management
				.setName(getManagementServiceName(properties, context.getEnvironment()));
		management.setPort(getManagementPort(properties, context));
		management.setTags(properties.getManagementTags());
		if (properties.isRegisterHealthCheck()) {
    
    
			management.setCheck(createCheck(getManagementPort(properties, context),
					heartbeatProperties, properties));
		}
		ConsulAutoRegistration registration = new ConsulAutoRegistration(management,
				autoServiceRegistrationProperties, properties, context,
				heartbeatProperties, managementRegistrationCustomizers);
		managementCustomize(managementRegistrationCustomizers, registration);
		return registration;
	}

	public static void managementCustomize(
			List<ConsulManagementRegistrationCustomizer> registrationCustomizers,
			ConsulAutoRegistration registration) {
    
    
		if (registrationCustomizers != null) {
    
    
			for (ConsulManagementRegistrationCustomizer customizer : registrationCustomizers) {
    
    
				customizer.customize(registration);
			}
		}
	}

	public static String getManagementServiceId(ConsulDiscoveryProperties properties,
			ApplicationContext context) {
    
    
		final String instanceId = properties.getInstanceId();
		if (StringUtils.hasText(instanceId)) {
    
    
			return normalizeForDns(
					instanceId + SEPARATOR + properties.getManagementSuffix());
		}
		return normalizeForDns(
				IdUtils.getDefaultInstanceId(context.getEnvironment(), false)) + SEPARATOR
				+ properties.getManagementSuffix();
	}

	public static String getManagementServiceName(ConsulDiscoveryProperties properties,
			Environment env) {
    
    
		final String appName = properties.getServiceName();
		if (StringUtils.hasText(appName)) {
    
    
			return normalizeForDns(
					appName + SEPARATOR + properties.getManagementSuffix());
		}
		return normalizeForDns(getAppName(properties, env)) + SEPARATOR
				+ properties.getManagementSuffix();
	}
	
	public void initializePort(int knownPort) {
    
    
		if (getService().getPort() == null) {
    
    
			getService().setPort(knownPort);
		}
		setCheck(getService(), this.autoServiceRegistrationProperties, getProperties(),
				this.context, this.heartbeatProperties);
	}

	public ConsulAutoRegistration managementRegistration() {
    
    
		return managementRegistration(this.autoServiceRegistrationProperties,
				getProperties(), this.context, this.managementRegistrationCustomizers,
				this.heartbeatProperties);
	}

}

3.2.2、NewService

NewService 并不能控制应用服务在consul集群中的注册。可以控制应用服务是否在consul UI中展示的问题以及健康监测Check相关参数的设置。
NewService中的端口是用于健康监测。

  1. 步骤 #3.2.1 ConsulDiscoveryProperties中属性port如果没有显式设置

1、步骤#3.2.1中NewService实例化过程中不会设置健康检查相关属性配置。
2、#4.1.1 重新设置NewService涉及的port以及健康检查相关属性。

  1. 步骤 #3.2.1 ConsulDiscoveryProperties中属性port如果存在显式设置,则不会存在步骤1中引用的执行。

4、服务注册监听器之ConsulAutoServiceRegistrationListener

public class ConsulAutoServiceRegistrationListener implements SmartApplicationListener {
    
    
	// ConsulAutoServiceRegistration
	private final ConsulAutoServiceRegistration autoServiceRegistration;
	@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    
    
		return WebServerInitializedEvent.class.isAssignableFrom(eventType);
	}
	@Override
	public void onApplicationEvent(ApplicationEvent applicationEvent) {
    
    
		if (applicationEvent instanceof WebServerInitializedEvent) {
    
    
			WebServerInitializedEvent event = (WebServerInitializedEvent) applicationEvent;
			ApplicationContext context = event.getApplicationContext();
			//autoServiceRegistration:ConsulAutoServiceRegistration
			this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort());//设置端口
			this.autoServiceRegistration.start();// 开始注册 
		}
	}
}
  1. ConsulAutoServiceRegistrationListener是监听WebServerInitializedEvent事件的监听器。
  2. 服务注册是通过事件发布机制触发的。其原理参考WebServerInitializedEvent事件
  3. 设置AbstractAutoServiceRegistration中属性port值为当前应用服务的端口。

4.1、AbstractAutoServiceRegistration

public void start() {
    
    
	if (!this.running.get()) {
    
    
		this.context.publishEvent(
				new InstancePreRegisteredEvent(this, getRegistration()));
		register();//继续注册,调用当前抽象类的子类ConsulAutoServiceRegistration
		if (shouldRegisterManagement()) {
    
    
			registerManagement();
		}
		this.context.publishEvent(
				new InstanceRegisteredEvent<>(this, getConfiguration()));
		this.running.compareAndSet(false, true);
	}

}

针对InstancePreRegisteredEvent、InstanceRegisteredEvent事件,应用服务可以实现对应的监听器,在服务注册前后获取相关的注册信息。

4.1.1、尝试设置NewService相关属性

protected ConsulAutoRegistration getRegistration() {
    
    
	//registration:ConsulAutoRegistration
	if (this.registration.getService().getPort() == null
			&& this.getPort().get() > 0) {
    
    
		this.registration.initializePort(this.getPort().get());
	}
	return this.registration;
}
  1. this.registration.getService():指的是NewService,应用服务实例的抽象。
  2. 如果NewService中端口号没有设置,则初始化为应用服务的端口号。
  3. 如果NewService中端口号没有设置,则初始化用于对应用服务健康检查的相关check信息。

4.1.2、ConsulServiceRegistry

// ConsulAutoServiceRegistration#register
protected void register() {
    
    
	if (!this.properties.isRegister()) {
    
    //ConsulDiscoveryProperties
		return;
	}
	super.register();//AbstractAutoServiceRegistration#register
}

spring.cloud.consul.discovery.register = false ,该配置控制当前应用服务是否在consul UI界面展示,其应用服务正常加入consul集群提供服务。
spring.cloud.consul.enabled = false,该配置当前应用服务是否加入consul集群。

protected void register() {
    
    
	// 3.1 #ConsulServiceRegistry
	this.serviceRegistry.register(getRegistration());
}

4.2、ConsulServiceRegistry

public void register(ConsulRegistration reg) {
    
    
	this.client.agentServiceRegister(reg.getService(),this.properties.getAclToken());//
	NewService service = reg.getService();
	if (this.heartbeatProperties.isEnabled() && this.ttlScheduler != null
			&& service.getCheck() != null
			&& service.getCheck().getTtl() != null) {
    
    
		this.ttlScheduler.add(reg.getInstanceId());
	}
}
  1. client为ConsulClient。
  2. 注册地址:http://localhost:8500/v1/agent/service/register。

5、健康检查Check

#3.2.1处理应用服务的健康检查。

public static void setCheck(NewService service,
		AutoServiceRegistrationProperties autoServiceRegistrationProperties,
		ConsulDiscoveryProperties properties, ApplicationContext context,
		HeartbeatProperties heartbeatProperties) {
    
    
	if (properties.isRegisterHealthCheck() && service.getCheck() == null) {
    
    
		Integer checkPort;
		if (shouldRegisterManagement(autoServiceRegistrationProperties, properties,
				context)) {
    
    
			checkPort = getManagementPort(properties, context);
		}
		else {
    
    
			checkPort = service.getPort();
		}
		Assert.notNull(checkPort, "checkPort may not be null");
		service.setCheck(createCheck(checkPort, heartbeatProperties, properties));
	}
}

应用服务存在健康监测的前提:

  1. ConsulDiscoveryProperties中显式设置端口号。
  2. ConsulDiscoveryProperties中属性registerHealthCheck为TRUE。如果为FALSE则consul中不存在对应用服务相关的健康监测。
public static NewService.Check createCheck(Integer port,
		HeartbeatProperties ttlConfig, ConsulDiscoveryProperties properties) {
    
    
	NewService.Check check = new NewService.Check();
	if (StringUtils.hasText(properties.getHealthCheckCriticalTimeout())) {
    
    // 健康检查响应临界时间,超过该时间服务将被剔除
		check.setDeregisterCriticalServiceAfter(
				properties.getHealthCheckCriticalTimeout());
	}
	if (ttlConfig.isEnabled()) {
    
    
		check.setTtl(ttlConfig.getTtl());
		return check;
	}

	if (properties.getHealthCheckUrl() != null) {
    
    
		check.setHttp(properties.getHealthCheckUrl());
	}
	else {
    
    
		check.setHttp(String.format("%s://%s:%s%s", properties.getScheme(),
				properties.getHostname(), port, properties.getHealthCheckPath()));
	}
	check.setHeader(properties.getHealthCheckHeaders());
	check.setInterval(properties.getHealthCheckInterval());//健康监测间隔时间
	check.setTimeout(properties.getHealthCheckTimeout());//健康监测超时时间
	check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify());
	return check;
}

spring.cloud.consul.discovery.register=true #4.1.2
spring.cloud.consul.discovery.register-health-check=true
spring.cloud.consul.discovery.register-health-check=true

6、Management

如果配置类ConsulDiscoveryProperties的属性healthCheckUrl显式设置,则该种方式获取的端口没有任何意义。

public static boolean shouldRegisterManagement(
		AutoServiceRegistrationProperties autoServiceRegistrationProperties,
		ConsulDiscoveryProperties properties, ApplicationContext context) {
    
    
		// registerManagement默认为TRUE
	return autoServiceRegistrationProperties.isRegisterManagement()
			&& getManagementPort(properties, context) != null
			&& ManagementServerPortUtils.isDifferent(context);
}

6.1、getManagementPort

AutoServiceRegistrationProperties:spring.cloud.service-registry.auto-registration

通过Management获取端口号方式:

  1. AutoServiceRegistrationProperties配置类属性registerManagement为TRUE。
  2. ConsulDiscoveryProperties配置类属性managementPort是否存在。
  3. ManagementServerProperties配置类属性port是否存在。
public static Integer getManagementPort(ConsulDiscoveryProperties properties,
		ApplicationContext context) {
    
    
	// If an alternate external port is specified, use it instead
	if (properties.getManagementPort() != null) {
    
    
		return properties.getManagementPort();
	}
	return ManagementServerPortUtils.getPort(context);
}
public static Integer getPort(BeanFactory beanFactory) {
    
    
	ManagementServerProperties properties = beanFactory.getBean(ManagementServerProperties.class);
	return properties.getPort();
}

6.2、ManagementServerPortUtils.isDifferent

public static boolean isDifferent(BeanFactory beanFactory) {
    
    
	return get(beanFactory) == ManagementServerPort.DIFFERENT;
}
public static ManagementServerPort get(BeanFactory beanFactory) {
    
    
	serverProperties = beanFactory.getBean(ServerProperties.class);//为了获取应用服务的端口
	ManagementServerProperties managementServerProperties = beanFactory.getBean(ManagementServerProperties.class);
	Integer port = managementServerProperties.getPort();
	if (port != null && port < 0) {
    
    
		return DISABLE;
	}
	if (!(beanFactory instanceof WebApplicationContext)) {
    
    
		// Current context is not a webapp
		return DIFFERENT;
	}
	return ((port == null)
			|| (serverProperties.getPort() == null && port.equals(8080))
			|| (port != 0 && port.equals(serverProperties.getPort())) ? SAME
					: DIFFERENT);
}

猜你喜欢

转载自blog.csdn.net/qq_36851469/article/details/127868517