SpringCloud之Consul源码解析
创始人
2024-01-26 07:13:44

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包涉及核心类如下:ConsulConfigServerAutoConfiguration、ConsulServiceRegistryAutoConfiguration等。

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:ThreadPoolTaskSchedulerthis.watchFuture = this.taskScheduler.scheduleWithFixedDelay(this::watchConfigKeyValues, this.properties.getWatch().getDelay());}}public void watchConfigKeyValues() {if (this.running.get()) {for (String context : this.consulIndexes.keySet()) {Response> 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@ConditionalOnMissingBeanpublic ConsulProperties consulProperties() {return new ConsulProperties();}@Bean@ConditionalOnMissingBeanpublic 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客户端接口的相关属性。

2、ConsulServiceRegistryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.enabled",matchIfMissing = true)
@AutoConfigureBefore(ServiceRegistryAutoConfiguration.class)
public class ConsulServiceRegistryAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient,ConsulDiscoveryProperties properties, HeartbeatProperties heartbeatProperties,@Autowired(required = false) TtlScheduler ttlScheduler) {return new ConsulServiceRegistry(consulClient, properties, ttlScheduler,heartbeatProperties);}@Bean@ConditionalOnMissingBeanpublic HeartbeatProperties heartbeatProperties() {return new HeartbeatProperties();}@Bean@ConditionalOnMissingBean// TODO: Split appropriate values to service-registry for Edgwarepublic ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {return new ConsulDiscoveryProperties(inetUtils);}}

3、ConsulAutoServiceRegistrationAutoConfiguration

@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 {@AutowiredAutoServiceRegistrationProperties autoServiceRegistrationProperties;@Bean@ConditionalOnMissingBeanpublic ConsulAutoServiceRegistration consulAutoServiceRegistration(ConsulServiceRegistry registry,AutoServiceRegistrationProperties autoServiceRegistrationProperties,ConsulDiscoveryProperties properties,ConsulAutoRegistration consulRegistration) {return new ConsulAutoServiceRegistration(registry,autoServiceRegistrationProperties, properties, consulRegistration);}@Beanpublic ConsulAutoServiceRegistrationListener consulAutoServiceRegistrationListener(ConsulAutoServiceRegistration registration) {return new ConsulAutoServiceRegistrationListener(registration);}@Bean@ConditionalOnMissingBeanpublic ConsulAutoRegistration consulRegistration(AutoServiceRegistrationProperties autoServiceRegistrationProperties,ConsulDiscoveryProperties properties, ApplicationContext applicationContext,ObjectProvider> registrationCustomizers,ObjectProvider> managementRegistrationCustomizers,HeartbeatProperties heartbeatProperties) {return ConsulAutoRegistration.registration(autoServiceRegistrationProperties,properties, applicationContext, registrationCustomizers.getIfAvailable(),managementRegistrationCustomizers.getIfAvailable(), heartbeatProperties);}@Configuration(proxyBeanMethods = false)@ConditionalOnClass(ServletContext.class)protected static class ConsulServletConfiguration {@Beanpublic ConsulRegistrationCustomizer servletConsulCustomizer(ObjectProvider servletContext) {return new ConsulServletRegistrationCustomizer(servletContext);}}}

3.1、ConsulAutoRegistration

public class ConsulAutoRegistration extends ConsulRegistration {/*** Instance ID separator.*/public static final char SEPARATOR = '-';private final AutoServiceRegistrationProperties autoServiceRegistrationProperties;private final ApplicationContext context;private final HeartbeatProperties heartbeatProperties;private final List managementRegistrationCustomizers;public static ConsulAutoRegistration registration(AutoServiceRegistrationProperties autoServiceRegistrationProperties,ConsulDiscoveryProperties properties, ApplicationContext context,List registrationCustomizers,List managementRegistrationCustomizers,HeartbeatProperties heartbeatProperties) {NewService service = new NewService();// NewService是consul中对于服务实例的抽象String appName = getAppName(properties, context.getEnvironment());//spring.application.nameservice.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());// we know the port and can set the checksetCheck(service, autoServiceRegistrationProperties, properties, context,heartbeatProperties);}ConsulAutoRegistration registration = new ConsulAutoRegistration(service,autoServiceRegistrationProperties, properties, context,heartbeatProperties, managementRegistrationCustomizers);customize(registrationCustomizers, registration);return registration;}public static void customize(List registrationCustomizers,ConsulAutoRegistration registration) {if (registrationCustomizers != null) {for (ConsulRegistrationCustomizer customizer : registrationCustomizers) {customizer.customize(registration);}}}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();}service.setCheck(createCheck(checkPort, heartbeatProperties, properties));}}public static ConsulAutoRegistration managementRegistration(AutoServiceRegistrationProperties autoServiceRegistrationProperties,ConsulDiscoveryProperties properties, ApplicationContext context,List 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 registrationCustomizers,ConsulAutoRegistration registration) {if (registrationCustomizers != null) {for (ConsulManagementRegistrationCustomizer customizer : registrationCustomizers) {customizer.customize(registration);}}}public static String getInstanceId(ConsulDiscoveryProperties properties,ApplicationContext context) {if (!StringUtils.hasText(properties.getInstanceId())) {return normalizeForDns(IdUtils.getDefaultInstanceId(context.getEnvironment(),properties.isIncludeHostnameInInstanceId()));}return normalizeForDns(properties.getInstanceId());}public static String normalizeForDns(String s) {...StringBuilder normalized = new StringBuilder();Character prev = null;for (char curr : s.toCharArray()) {Character toAppend = null;if (Character.isLetterOrDigit(curr)) {toAppend = curr;}else if (prev == null || !(prev == SEPARATOR)) {toAppend = SEPARATOR;}if (toAppend != null) {normalized.append(toAppend);prev = toAppend;}}return normalized.toString();}public static List createTags(ConsulDiscoveryProperties properties) {List tags = new LinkedList<>(properties.getTags());if (!StringUtils.isEmpty(properties.getInstanceZone())) {tags.add(properties.getDefaultZoneMetadataName() + "="+ properties.getInstanceZone());}if (!StringUtils.isEmpty(properties.getInstanceGroup())) {tags.add("group=" + properties.getInstanceGroup());}tags.add("secure="+ Boolean.toString(properties.getScheme().equalsIgnoreCase("https")));return tags;}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;}public static String getAppName(ConsulDiscoveryProperties properties,Environment env) {final String appName = properties.getServiceName();if (StringUtils.hasText(appName)) {return appName;}return env.getProperty("spring.application.name", "application");}public static boolean shouldRegisterManagement(AutoServiceRegistrationProperties autoServiceRegistrationProperties,ConsulDiscoveryProperties properties, ApplicationContext context) {return autoServiceRegistrationProperties.isRegisterManagement()&& getManagementPort(properties, context) != null&& ManagementServerPortUtils.isDifferent(context);}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 static Integer getManagementPort(ConsulDiscoveryProperties properties,ApplicationContext context) {// If an alternate external port is specified, use it insteadif (properties.getManagementPort() != null) {return properties.getManagementPort();}return ManagementServerPortUtils.getPort(context);}public void initializePort(int knownPort) {if (getService().getPort() == null) {// not set by propertiesgetService().setPort(knownPort);}// we might not have a port until now, so this is the earliest we// can create a checksetCheck(getService(), this.autoServiceRegistrationProperties, getProperties(),this.context, this.heartbeatProperties);}public ConsulAutoRegistration managementRegistration() {return managementRegistration(this.autoServiceRegistrationProperties,getProperties(), this.context, this.managementRegistrationCustomizers,this.heartbeatProperties);}}

5、服务注册之ConsulAutoServiceRegistrationListener

public class ConsulAutoServiceRegistrationListener implements SmartApplicationListener {// ConsulAutoServiceRegistrationprivate final ConsulAutoServiceRegistration autoServiceRegistration;public ConsulAutoServiceRegistrationListener(ConsulAutoServiceRegistration autoServiceRegistration) {this.autoServiceRegistration = autoServiceRegistration;}@Overridepublic boolean supportsEventType(Class eventType) {return WebServerInitializedEvent.class.isAssignableFrom(eventType);}@Overridepublic boolean supportsSourceType(Class sourceType) {return true;}@Overridepublic void onApplicationEvent(ApplicationEvent applicationEvent) {if (applicationEvent instanceof WebServerInitializedEvent) {WebServerInitializedEvent event = (WebServerInitializedEvent) applicationEvent;ApplicationContext context = event.getApplicationContext();this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort());this.autoServiceRegistration.start();// 开始注册 }}
}
  1. ConsulAutoServiceRegistrationListener是监听WebServerInitializedEvent事件的监听器。
  2. 服务注册是通过事件发布机制触发的。其原理参考WebServerInitializedEvent事件

5.1、AbstractAutoServiceRegistration

public void start() {// only initialize if nonSecurePort is greater than 0 and it isn't already running// because of containerPortInitializer belowif (!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);}}

5.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。

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
插入行快捷键 excel添加空... 如下图所示,像这样在两行之间插入多行空白行的操作,你会几种?可能很多人都会觉得,不就是插入空行嘛,简...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
疑难件什么意思 淘宝退货为什么... 01 常见售后类型对于现在主流电商来说,通常支持的售后形式包括仅退款,退货退款,换货,补寄。用户根据...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...