spring cloud集成 consul源码分析
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 found
String 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 anything
if (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 primed
if (!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 error
if (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 failure
log.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 {
@Autowired
private ConsulClient consul; @Bean
public ConsulConfigProperties consulConfigProperties() {
return new ConsulConfigProperties();
} @Bean
public 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
@ConditionalOnMissingBean
public ConsulProperties consulProperties() {
return new ConsulProperties();
} @Bean
@ConditionalOnMissingBean
public 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.ConsulDiscoveryClientConfiguration org.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; @PostConstruct
public 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
@ConditionalOnMissingBean
public ConsulAutoServiceRegistration consulAutoServiceRegistration(ConsulServiceRegistry registry, ConsulDiscoveryProperties properties, ConsulAutoRegistration consulRegistration) {
return new ConsulAutoServiceRegistration(registry, properties, consulRegistration);
} @Bean
@ConditionalOnMissingBean
public 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
@ConditionalOnMissingBean
public 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
@ConditionalOnMissingBean
public HeartbeatProperties heartbeatProperties() {
return new HeartbeatProperties();
} @Bean
@ConditionalOnMissingBean
public 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 { @Autowired
private ConsulClient consulClient; @Autowired(required = false)
private ServerProperties serverProperties; @Bean
@ConditionalOnMissingBean
@ConditionalOnProperty("spring.cloud.consul.discovery.heartbeat.enabled")
//TODO: move to service-registry for Edgware
public TtlScheduler ttlScheduler(HeartbeatProperties heartbeatProperties) {
return new TtlScheduler(heartbeatProperties, consulClient);
} @Bean
//TODO: move to service-registry for Edgware
public HeartbeatProperties heartbeatProperties() {
return new HeartbeatProperties();
} @Bean
//TODO: Split appropriate values to service-registry for Edgware
public ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {
return new ConsulDiscoveryProperties(inetUtils);
} @Bean
@ConditionalOnMissingBean
public ConsulDiscoveryClient consulDiscoveryClient(ConsulDiscoveryProperties discoveryProperties, final ApplicationContext context) {
ConsulDiscoveryClient discoveryClient = new ConsulDiscoveryClient(consulClient,
discoveryProperties, new LifecycleRegistrationResolver(context));
discoveryClient.setServerProperties(serverProperties); //null ok
return discoveryClient;
} class LifecycleRegistrationResolver implements ConsulDiscoveryClient.LocalResolver {
private ApplicationContext context; public LifecycleRegistrationResolver(ApplicationContext context) {
this.context = context;
} @Override
public 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");
} @Override
public 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 { @Bean
public ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {
ConsulDiscoveryProperties properties = new ConsulDiscoveryProperties(inetUtils);
// for bootstrap, lifecycle (and hence registration) is not needed, just discovery client
properties.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源码分析的更多相关文章
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- spring boot 2.0 源码分析(四)
在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...
- spring boot 2.0 源码分析(一)
在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...
- Spring JPA实现逻辑源码分析总结
1.SharedEntityManagerCreator: entitymanager的创建入口 该类被EntityManagerBeanDefinitionRegistrarPostProcesso ...
- Spring中Bean命名源码分析
Spring中Bean命名源码分析 一.案例代码 首先是demo的整体结构 其次是各个部分的代码,代码本身比较简单,不是我们关注的重点 配置类 /** * @Author Helius * @Crea ...
- Spring Boot 自动配置 源码分析
Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
- spring boot 2.0 源码分析(二)
在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /** * Run the ...
- spring boot 2.0 源码分析(三)
通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...
随机推荐
- java之IO处理
File文件基础 文件与文件夹抽象路径名称的表示.其构造方法有四个 File(File parent,String child):从抽象父文件夹下创建一个File实例. File(String par ...
- 安卓ProgressBar水平进度条的颜色设置
安卓系统提供了水平进度条ProgressBar的样式,而我们在实际开发中,差点儿不可能使用默认的样式.原因就是"太丑"^_^ 所以我们在很多其它的时候须要对其颜色进行自己定义,主要 ...
- java mail邮件发送(带附件) 支持SSL
java mail邮件发送(带附件)有三个类 MailSenderInfo.java package mail; import java.util.Properties; import java.ut ...
- node之版本号升级和管理
如今非常多人预计和我一样项目中已经開始应用起nodeJS,而伴随着项目的需求,对nodejs版本号也有着各种需求.好了直接进入主题,如今node版本号管理网上有非常多方式.这里说两种: 第一种modu ...
- bzoj5105: [CodePlus2017]晨跑(LCM)
5105: [CodePlus2017]晨跑 题目:传送门 题解: 没有很懂Code Puls 的操作...一道签到的三个数的LCM??? 代码: #include<cstdio> #in ...
- gson的安装和使用
gson的安装和使用 1.安装 2.布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...
- JavaScript原型链:prototype与__proto__
title: 'JavaScript原型链:prototype与__proto__' toc: false date: 2018-09-04 11:16:54 主要看了这一篇,讲解的很清晰,最主要的一 ...
- shiro动态控制url资源
怎么利用shiro权限动态控制每个url资源呢?主要包括jsp(html)页面.action的url访问,而静态资源和登录资源则可直接访问. 所谓动态控制url就是url的权限控制不是手动写死在配置文 ...
- 模仿百度首页“元宵节汤圆”动图(js的定时任务:setInterval)
模仿百度首页“元宵节汤圆”动图:(js的定时任务:setInterval) 原理:需要一张切图,通过不断定位使得图片就像一帧一帧的图片在播放从而形成了动画 效果图: 切图地址: https://ss1 ...
- Html表单提交到Servlet输出到页面乱码
Html使用的编码是UTF-8编码显示页面,之后使用form表单提交字段到Servlet中,Servlet将利用getParamer方法获得form提交的字段,之后通过Respone中的writer将 ...