博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring cloud集成 consul源码分析
阅读量:6968 次
发布时间:2019-06-27

本文共 20523 字,大约阅读时间需要 68 分钟。

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 ()

Supports all API endpoints (), all consistency modes and parameters (tags, datacenters etc.)

1.3 整体架构

 

2.源码分析

主要工程:

2.1 spring-cloud-consul-config

spring.factories

# Auto Configurationorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration# Bootstrap Configurationorg.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
> 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@ConditionalOnConsulEnabledpublic 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
profiles = Arrays.asList(env.getActiveProfiles()); String prefix = this.properties.getPrefix(); List
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
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 Configurationorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.consul.ConsulAutoConfiguration

实现如下:

@Configuration@EnableConfigurationProperties@ConditionalOnConsulEnabledpublic 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 Configurationorg.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;    @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)@EnableConfigurationPropertiespublic 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 getBean(Class
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

转载地址:http://ytisl.baihongyu.com/

你可能感兴趣的文章
ES搭建
查看>>
长sql与sql执行进度
查看>>
Java命名规范
查看>>
Openwrt通过tc,iptalbes限速
查看>>
get_hard_info.sh
查看>>
从0开始
查看>>
腾讯网络技术TGW
查看>>
Windows Server入门系列之十二 进程管理
查看>>
搭建DHCP服务器
查看>>
Linux的watch命令 — 实时监测命令的运行结果
查看>>
IO多路复用之epoll总结
查看>>
关于计算机脱域
查看>>
cisco c4507R-E 交换机启动不起来
查看>>
java web 自定义标签
查看>>
2019活动技术(Eventech)的新趋势
查看>>
mac系统安装/升级node
查看>>
数据库-----同义词
查看>>
python venv actieve uninstall pack-name sitepage
查看>>
10月份技术:PXE批量安装LINUX系统
查看>>
linux head
查看>>