1.简介

1.1 Consul is a tool for service discovery and configuration. Consul is distributed, highly available, and extremely scalable.

Consul provides several key features:

  • Service Discovery - Consul makes it simple for services to register themselves and to discover other services via a DNS or HTTP interface. External services such as SaaS providers can be registered as well.

  • Health Checking - Health Checking enables Consul to quickly alert operators about any issues in a cluster. The integration with service discovery prevents routing traffic to unhealthy hosts and enables service level circuit breakers.

  • Key/Value Storage - A flexible key/value store enables storing dynamic configuration, feature flagging, coordination, leader election and more. The simple HTTP API makes it easy to use anywhere.

  • Multi-Datacenter - Consul is built to be datacenter aware, and can support any number of regions without complex configuration.

Consul runs on Linux, Mac OS X, FreeBSD, Solaris, and Windows.

1.2 consul-api

Java client for Consul HTTP API (http://consul.io)

Supports all API endpoints (http://www.consul.io/docs/agent/http.html), all consistency modes and parameters (tags, datacenters etc.)

1.3 整体架构

2.源码分析

主要工程:

2.1 spring-cloud-consul-config

spring.factories

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigBootstrapConfiguration

2.1.1 ConsulConfigAutoConfiguration自动配置

@Configuration
@ConditionalOnConsulEnabled
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)
public class ConsulConfigAutoConfiguration {@Configuration@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) {return new ConfigWatch(properties, consul, locator.getContextIndexes());}}
}

其中,ConfigWatch的主要代码如下:

    @Scheduled(fixedDelayString = "${spring.cloud.consul.config.watch.delay:1000}")public void watchConfigKeyValues() {if (this.running.get()) {for (String context : this.consulIndexes.keySet()) {// turn the context into a Consul folder path (unless our config format are FILES)if (properties.getFormat() != FILES && !context.endsWith("/")) {context = context + "/";}try {Long currentIndex = this.consulIndexes.get(context);if (currentIndex == null) {currentIndex = -1L;}// use the consul ACL token if foundString aclToken = properties.getAclToken();if (StringUtils.isEmpty(aclToken)) {aclToken = null;}Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken,new QueryParams(this.properties.getWatch().getWaitTime(),currentIndex));// if response.value == null, response was a 404, otherwise it was a 200// reducing churn if there wasn't anythingif (response.getValue() != null && !response.getValue().isEmpty()) {Long newIndex = response.getConsulIndex();if (newIndex != null && !newIndex.equals(currentIndex)) {// don't publish the same index again, don't publish the first time (-1) so index can be primedif (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));}this.consulIndexes.put(context, newIndex);}}} catch (Exception e) {// only fail fast on the initial query, otherwise just log the errorif (firstTime && this.properties.isFailFast()) {log.error("Fail fast is set and there was an error reading configuration from consul.");ReflectionUtils.rethrowRuntimeException(e);} else if (log.isTraceEnabled()) {log.trace("Error querying consul Key/Values for context '" + context + "'", e);} else if (log.isWarnEnabled()) {// simplified one line log message in the event of an agent failurelog.warn("Error querying consul Key/Values for context '" + context + "'. Message: " + e.getMessage());}}}}firstTime = false;}

2.1.2 启动配置ConsulConfigBootstrapConfiguration

定义:

@Configuration
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {@Configuration@EnableConfigurationProperties@Import(ConsulAutoConfiguration.class)@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)protected static class ConsulPropertySourceConfiguration {@Autowiredprivate ConsulClient consul;@Beanpublic ConsulConfigProperties consulConfigProperties() {return new ConsulConfigProperties();}@Beanpublic ConsulPropertySourceLocator consulPropertySourceLocator(ConsulConfigProperties consulConfigProperties) {return new ConsulPropertySourceLocator(consul, consulConfigProperties);}}
}

其中,ConsulPropertySourceLocator主要流程

    @Override@Retryable(interceptor = "consulRetryInterceptor")public PropertySource<?> locate(Environment environment) {if (environment instanceof ConfigurableEnvironment) {ConfigurableEnvironment env = (ConfigurableEnvironment) environment;RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env);String appName = properties.getName();if (appName == null) {appName = propertyResolver.getProperty("spring.application.name");}List<String> profiles = Arrays.asList(env.getActiveProfiles());String prefix = this.properties.getPrefix();List<String> suffixes = new ArrayList<>();if (this.properties.getFormat() != FILES) {suffixes.add("/");} else {suffixes.add(".yml");suffixes.add(".yaml");suffixes.add(".properties");}String defaultContext = prefix + "/" + this.properties.getDefaultContext();for (String suffix : suffixes) {this.contexts.add(defaultContext + suffix);}for (String suffix : suffixes) {addProfiles(this.contexts, defaultContext, profiles, suffix);}String baseContext = prefix + "/" + appName;for (String suffix : suffixes) {this.contexts.add(baseContext + suffix);}for (String suffix : suffixes) {addProfiles(this.contexts, baseContext, profiles, suffix);}Collections.reverse(this.contexts);CompositePropertySource composite = new CompositePropertySource("consul");for (String propertySourceContext : this.contexts) {try {ConsulPropertySource propertySource = null;if (this.properties.getFormat() == FILES) {Response<GetValue> response = this.consul.getKVValue(propertySourceContext, this.properties.getAclToken());addIndex(propertySourceContext, response.getConsulIndex());if (response.getValue() != null) {ConsulFilesPropertySource filesPropertySource = new ConsulFilesPropertySource(propertySourceContext, this.consul, this.properties);filesPropertySource.init(response.getValue());propertySource = filesPropertySource;}} else {propertySource = create(propertySourceContext, contextIndex);}if (propertySource != null) {composite.addPropertySource(propertySource);}} catch (Exception e) {if (this.properties.isFailFast()) {log.error("Fail fast is set and there was an error reading configuration from consul.");ReflectionUtils.rethrowRuntimeException(e);} else {log.warn("Unable to load consul config from "+ propertySourceContext, e);}}}return composite;}return null;}

2.2 spring-cloud-consul-core

spring.factories

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.ConsulAutoConfiguration

实现如下:

@Configuration
@EnableConfigurationProperties
@ConditionalOnConsulEnabled
public class ConsulAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic ConsulProperties consulProperties() {return new ConsulProperties();}@Bean@ConditionalOnMissingBeanpublic ConsulClient consulClient(ConsulProperties consulProperties) {return new ConsulClient(consulProperties.getHost(), consulProperties.getPort()); } @Configuration @ConditionalOnClass(Endpoint.class) protected static class ConsulHealthConfig { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint("consul") public ConsulEndpoint consulEndpoint(ConsulClient consulClient) { return new ConsulEndpoint(consulClient); } @Bean @ConditionalOnMissingBean @ConditionalOnEnabledHealthIndicator("consul") public ConsulHealthIndicator consulHealthIndicator(ConsulClient consulClient) { return new ConsulHealthIndicator(consulClient); } } @ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class }) @Configuration @EnableRetry(proxyTargetClass = true) @Import(AopAutoConfiguration.class) @EnableConfigurationProperties(RetryProperties.class) protected static class RetryConfiguration { @Bean(name = "consulRetryInterceptor") @ConditionalOnMissingBean(name = "consulRetryInterceptor") public RetryOperationsInterceptor consulRetryInterceptor( RetryProperties properties) { return RetryInterceptorBuilder .stateless() .backOffOptions(properties.getInitialInterval(), properties.getMultiplier(), properties.getMaxInterval()) .maxAttempts(properties.getMaxAttempts()).build(); } } }

定义ConsulProperties、consulClient、ConsulEndpoint、ConsulHealthIndicator、RetryOperationsInterceptor

2.3 spring-cloud-consul-discovery

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration,\
org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerAutoConfiguration,\
org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration,\
org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration# Discovery Client Configuration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfigurationorg.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.discovery.configclient.ConsulDiscoveryClientConfigServiceBootstrapConfiguration

2.3.1 ConsulRibbonClientConfiguration

@Configuration
@EnableConfigurationProperties
@ConditionalOnConsulEnabled
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnProperty(value = "spring.cloud.consul.ribbon.enabled", matchIfMissing = true)
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = ConsulRibbonClientConfiguration.class)
public class RibbonConsulAutoConfiguration {}

2.3.2 ConsulConfigServerAutoConfiguration for config server

/*** Extra configuration for config server if it happens to be registered with Consul.** @author Dave Syer*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ ConsulDiscoveryProperties.class, ConsulClient.class,ConfigServerProperties.class })
public class ConsulConfigServerAutoConfiguration {@Autowired(required = false)private ConsulDiscoveryProperties properties;@Autowired(required = false)private ConfigServerProperties server;@PostConstructpublic void init() {if (this.properties == null || this.server == null) {return;}String prefix = this.server.getPrefix();if (StringUtils.hasText(prefix)) {this.properties.getTags().add("configPath="+prefix);}}}

2.3.3 ConsulAutoServiceRegistrationAutoConfiguration

@Configuration
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnMissingBean(type = "org.springframework.cloud.consul.discovery.ConsulLifecycle")
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter(ConsulServiceRegistryAutoConfiguration.class)
public class ConsulAutoServiceRegistrationAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic ConsulAutoServiceRegistration consulAutoServiceRegistration(ConsulServiceRegistry registry, ConsulDiscoveryProperties properties, ConsulAutoRegistration consulRegistration) {return new ConsulAutoServiceRegistration(registry, properties, consulRegistration);}@Bean@ConditionalOnMissingBeanpublic ConsulAutoRegistration consulRegistration(ConsulDiscoveryProperties properties, ApplicationContext applicationContext,ServletContext servletContext, HeartbeatProperties heartbeatProperties) {return ConsulAutoRegistration.registration(properties, applicationContext, servletContext, heartbeatProperties);}}

定义了ConsulAutoServiceRegistration、ConsulAutoRegistration

2.3.4 ConsulServiceRegistryAutoConfiguration

@Configuration
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.enabled", matchIfMissing = true)
@AutoConfigureBefore(ServiceRegistryAutoConfiguration.class)
public class ConsulServiceRegistryAutoConfiguration {@Autowired(required = false)private TtlScheduler ttlScheduler;@Bean@ConditionalOnMissingBeanpublic ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient, ConsulDiscoveryProperties properties,HeartbeatProperties heartbeatProperties) {return new ConsulServiceRegistry(consulClient, properties, ttlScheduler, heartbeatProperties);}@Bean@ConditionalOnMissingBean@ConditionalOnProperty("spring.cloud.consul.discovery.heartbeat.enabled")public TtlScheduler ttlScheduler(ConsulClient consulClient, HeartbeatProperties heartbeatProperties) {return new TtlScheduler(heartbeatProperties, consulClient);}@Bean@ConditionalOnMissingBeanpublic HeartbeatProperties heartbeatProperties() {return new HeartbeatProperties();}@Bean@ConditionalOnMissingBeanpublic ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {return new ConsulDiscoveryProperties(inetUtils);}}

定义了 ConsulServiceRegistry、TtlScheduler、HeartbeatProperties、ConsulDiscoveryProperties

2.3.5 客户端发现ConsulDiscoveryClientConfiguration

@Configuration
@ConditionalOnConsulEnabled
@ConditionalOnProperty(value = "spring.cloud.consul.discovery.enabled", matchIfMissing = true)
@EnableConfigurationProperties
public class ConsulDiscoveryClientConfiguration {@Autowiredprivate ConsulClient consulClient;@Autowired(required = false)private ServerProperties serverProperties;@Bean@ConditionalOnMissingBean@ConditionalOnProperty("spring.cloud.consul.discovery.heartbeat.enabled")//TODO: move to service-registry for Edgwarepublic TtlScheduler ttlScheduler(HeartbeatProperties heartbeatProperties) {return new TtlScheduler(heartbeatProperties, consulClient);}@Bean//TODO: move to service-registry for Edgwarepublic HeartbeatProperties heartbeatProperties() {return new HeartbeatProperties();}@Bean//TODO: Split appropriate values to service-registry for Edgwarepublic ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {return new ConsulDiscoveryProperties(inetUtils);}@Bean@ConditionalOnMissingBeanpublic ConsulDiscoveryClient consulDiscoveryClient(ConsulDiscoveryProperties discoveryProperties, final ApplicationContext context) {ConsulDiscoveryClient discoveryClient = new ConsulDiscoveryClient(consulClient,discoveryProperties, new LifecycleRegistrationResolver(context));discoveryClient.setServerProperties(serverProperties); //null okreturn discoveryClient;}class LifecycleRegistrationResolver implements ConsulDiscoveryClient.LocalResolver {private ApplicationContext context;public LifecycleRegistrationResolver(ApplicationContext context) {this.context = context;}@Overridepublic String getInstanceId() {ConsulRegistration registration = getBean(ConsulRegistration.class);if (registration != null) {return registration.getInstanceId();}ConsulLifecycle lifecycle = getBean(ConsulLifecycle.class);if (lifecycle != null) {return lifecycle.getInstanceId();}throw new IllegalStateException("Must have one of ConsulRegistration or ConsulLifecycle");}@Overridepublic Integer getPort() {ConsulRegistration registration = getBean(ConsulRegistration.class);if (registration != null) {return registration.getService().getPort();}ConsulLifecycle lifecycle = getBean(ConsulLifecycle.class);if (lifecycle != null) {return lifecycle.getConfiguredPort();}throw new IllegalStateException("Must have one of ConsulRegistration or ConsulLifecycle");}<T> T getBean(Class<T> type) {try {return context.getBean(type);} catch (NoSuchBeanDefinitionException e) {}return null;}}@Bean@ConditionalOnMissingBean@ConditionalOnProperty(name = "spring.cloud.consul.discovery.catalogServicesWatch.enabled", matchIfMissing = true)public ConsulCatalogWatch consulCatalogWatch(ConsulDiscoveryProperties discoveryProperties) {return new ConsulCatalogWatch(discoveryProperties, consulClient);}
}

2.3.6 ConsulDiscoveryClientConfigServiceBootstrapConfiguration

/*** Helper for config client that wants to lookup the config server via discovery.** @author Spencer Gibb*/
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
@Configuration
@Import({ ConsulAutoConfiguration.class, ConsulDiscoveryClientConfiguration.class})
public class ConsulDiscoveryClientConfigServiceBootstrapConfiguration {@Beanpublic ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {ConsulDiscoveryProperties properties = new ConsulDiscoveryProperties(inetUtils);// for bootstrap, lifecycle (and hence registration) is not needed, just discovery clientproperties.getLifecycle().setEnabled(false);return properties;}
}

参考文献

【1】https://github.com/hashicorp/consul

【2】https://github.com/Ecwid/consul-api

【3】https://www.consul.io/api/index.html

spring cloud集成 consul源码分析相关推荐

  1. eureka 之前的服务如何关闭_干货分享 | 服务注册中心Spring Cloud Eureka部分源码分析...

    友情提示:全文13000多文字,预计阅读时间10-15分钟 Spring Cloud Eureka作为常用的服务注册中心,我们有必要去了解其内在实现机制,这样出现问题的时候我们可以快速去定位问题.当我 ...

  2. springboot集成mybatis源码分析-启动加载mybatis过程(二)

    springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...

  3. springboot集成mybatis源码分析(一)

    springboot集成mybatis源码分析(一) 本篇文章只是简单接受使用,具体源码解析请看后续文章 1.新建springboot项目,并导入mybatis的pom配置 配置数据库驱动和mybat ...

  4. spring cloud 集成consul

    spring cloud 集成consul pom.xml配置 <dependency><groupId>org.springframework.cloud</group ...

  5. springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)

    springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三) 例: package com.example.demo.service;import com.exa ...

  6. 深度思考 Spring Cloud + Alibaba Sentinel 源码原理

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. 作者 | 向寒 / 孙玄 来源 | 架构之美 ...

  7. spring boot 2.0 源码分析(二)

    在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /*** Run the ...

  8. spring boot 2.0 源码分析(三)

    通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...

  9. Spring Security CSRF防御源码分析

    一.CSRF简介 1.CSRF是什么? CSRF(Cross-site request forgery),也被称为:one click attack/session riding,中文名称:跨站请求伪 ...

最新文章

  1. Silverlight 2 beta 2 中目前不支持共享 WCF 的客户端类型
  2. 【干货分享】流程DEMO-外出申请
  3. blog摘录--测试感触
  4. 星型模型 3nf的区别_贵州省遵义市工业模型经典案例展示
  5. 第九周项目三-人数不定的工资类
  6. python的优点和缺点-Python语言的优缺点,你知道吗?
  7. 今晚直播 | 旷视研究院王毅:用于条件图像生成的注意力归一化
  8. elxel表格纸张尺寸_一本书的诞生:纸张知识
  9. 做系统的U盘如何格式化
  10. gitlab之主要目录介绍
  11. quartz mysql 驱动_quartz mysql 集成
  12. python做考勤表_考勤表下载免费后怎么制作?
  13. 浅谈,盘点历史上有哪些著名的电脑病毒,80%的人都不知道!
  14. Linux 系统升级蝉道
  15. 四柱八字大全 php,四柱八字
  16. 经纬度格式化转换-数据库函数方式
  17. Gym 101669J SEERC 2017 Cunning Friends
  18. thinkphp6 框架源码分析
  19. Day114.尚医通:用户认证、阿里云OSS、就诊人管理
  20. 大学英语六级词汇(笔记)

热门文章

  1. java基础初步总结
  2. Node.js 全局对象
  3. SSM中(Spring-SpringMVC-Mybatis)(一:概念)
  4. 前端开发应届生面试指南(含各大公司具体指南及面试真题)
  5. 现成Android 5.0系统源代码
  6. 前端小笔记:左定宽,右随意
  7. Linux下两个进程可以同时打开同一个文件,这时如下描述错误的是:
  8. error 图片,加载错误-》实用笔记
  9. 毒霸能清除的大小流氓清单(部分)
  10. RedMonk 语言排行:Kotlin 上升 8 位,TS 快进前 10