美文网首页
spring boot consul 客户端加载过程

spring boot consul 客户端加载过程

作者: 草祭木初 | 来源:发表于2021-04-08 10:32 被阅读0次

    1,引入

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
                <version>3.0.2</version>
            </dependency>
    

    主要是下面几个包,还有starter不写了

    com.ecwid.consul:consul-api:1.4.5
    org.springframework.cloud:spring-cloud-consul-core:3.0.2
    org.springframework.cloud:spring-cloud-consul-discovery:3.0.2
    

    配置

    spring.cloud.consul.host=xxx
    spring.cloud.consul.port=8500
    #注册到consul的服务名称
    spring.cloud.consul.discovery.service-name=${spring.application.name}
    spring.cloud.consul.discovery.prefer-ip-address=true
    spring.cloud.consul.discovery.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
    spring.cloud.consul.discovery.heartbeat.enabled=true
    spring.cloud.consul.discovery.health-check-interval=3s
    spring.cloud.consul.discovery.health-check-url=http://${spring.cloud.client.ip-address}:${server.port}/actuator/health
    spring.cloud.consul.enabled=true
    #服务停止时取消注册
    spring.cloud.consul.discovery.deregister=true
    #健康检查失败多长时间后,取消注册
    spring.cloud.consul.discovery.health-check-critical-timeout=10s
    

    consul用instance-id来区分实例的,同一个机器开多个服务都想注册到consul上就加上port
    这样服务名相同,配合OpenFeign 和 spring-cloud-loadbalancer就实现 负载均衡了

    上面的配置写好了就可以自动注册了

    2,spring-cloud-consul-core

    2.1 spring.fatories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.consul.ConsulAutoConfiguration
    

    2.2 ConsulAutoConfiguration

    主要功能:

    • 根据配置生成ConsulClient对象 后面都会用到它
    • 生成健康检查用到的对象 ConsulEndpoint,ConsulHealthIndicator
    • 生成重试拦截器 RetryOperationsInterceptor
    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties
    @ConditionalOnConsulEnabled
    public class ConsulAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean
        public ConsulProperties consulProperties() {
            return new ConsulProperties();
        }
    
        @Bean
        @ConditionalOnMissingBean
        public ConsulClient consulClient(ConsulProperties consulProperties) {
            return createConsulClient(consulProperties);
        }
    
        public static ConsulClient createConsulClient(ConsulProperties consulProperties) {
            final String agentPath = consulProperties.getPath();
            final String agentHost = StringUtils.hasLength(consulProperties.getScheme())
                    ? consulProperties.getScheme() + "://" + consulProperties.getHost() : consulProperties.getHost();
            final ConsulRawClient.Builder builder = ConsulRawClient.Builder.builder().setHost(agentHost)
                    .setPort(consulProperties.getPort());
    
            if (consulProperties.getTls() != null) {
                ConsulProperties.TLSConfig tls = consulProperties.getTls();
                TLSConfig tlsConfig = new TLSConfig(tls.getKeyStoreInstanceType(), tls.getCertificatePath(),
                        tls.getCertificatePassword(), tls.getKeyStorePath(), tls.getKeyStorePassword());
                builder.setTlsConfig(tlsConfig);
            }
    
            if (StringUtils.hasLength(agentPath)) {
                String normalizedAgentPath = StringUtils.trimTrailingCharacter(agentPath, '/');
                normalizedAgentPath = StringUtils.trimLeadingCharacter(normalizedAgentPath, '/');
    
                builder.setPath(normalizedAgentPath);
            }
    
            return new ConsulClient(builder.build());
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(Endpoint.class)
        @EnableConfigurationProperties(ConsulHealthIndicatorProperties.class)
        protected static class ConsulHealthConfig {
    
            @Bean
            @ConditionalOnMissingBean
            @ConditionalOnAvailableEndpoint
            public ConsulEndpoint consulEndpoint(ConsulClient consulClient) {
                return new ConsulEndpoint(consulClient);
            }
    
            @Bean
            @ConditionalOnMissingBean
            @ConditionalOnEnabledHealthIndicator("consul")
            public ConsulHealthIndicator consulHealthIndicator(ConsulClient consulClient,
                    ConsulHealthIndicatorProperties properties) {
                return new ConsulHealthIndicator(consulClient, properties);
            }
    
        }
    
        @ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class })
        @Configuration(proxyBeanMethods = false)
        @EnableRetry(proxyTargetClass = true)
        @Import(AopAutoConfiguration.class)
        @EnableConfigurationProperties(RetryProperties.class)
        @ConditionalOnProperty(value = "spring.cloud.consul.retry.enabled", matchIfMissing = true)
        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();
            }
    
        }
    
    }
    

    3,spring-cloud-consul-discovery

    3.1 spring.fatories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerAutoConfiguration,\
    org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration,\
    org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration,\
    org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration,\
    org.springframework.cloud.consul.discovery.reactive.ConsulReactiveDiscoveryClientConfiguration,\
    org.springframework.cloud.consul.discovery.ConsulCatalogWatchAutoConfiguration, \
    org.springframework.cloud.consul.support.ConsulHeartbeatAutoConfiguration
    
    org.springframework.cloud.bootstrap.BootstrapConfiguration=\
    org.springframework.cloud.consul.discovery.configclient.ConsulDiscoveryClientConfigServiceBootstrapConfiguration
    
    org.springframework.boot.Bootstrapper=\
    org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper
    

    下面这3个自动配置,是跟配置中心相关的
    这篇文章不讨论

    org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerAutoConfiguration
    
    org.springframework.cloud.bootstrap.BootstrapConfiguration=\
    org.springframework.cloud.consul.discovery.configclient.ConsulDiscoveryClientConfigServiceBootstrapConfiguration
    
    org.springframework.boot.Bootstrapper=\
    org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper
    

    3.2 ConsulServiceRegistryAutoConfiguration

    它要在ServiceRegistryAutoConfiguration 之前加载
    ServiceRegistryAutoConfiguration是spring-cloud-commons包下的 等下看
    spring-cloud-commons:它的主要功能

    • DiscoveryClient接口
    • ServiceRegistry接口
    • RestTemplate用于使用DiscoveryClient解析主机名的工具

    ConsulServiceRegistryAutoConfiguration
    主要功能:

    • 生成ConsulServiceRegistry
      consulServiceRegistry
      需要的参数 consulClient 来自 2,spring-cloud-consul-core 里生产的
      返回值ConsulServiceRegistry 实现了ServiceRegistry 接口,要给ServiceRegistryAutoConfiguration用
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnConsulEnabled
    @Conditional(ConsulServiceRegistryAutoConfiguration.OnConsulRegistrationEnabledCondition.class)
    @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);
        }
    
        protected static class OnConsulRegistrationEnabledCondition extends AllNestedConditions {
                。。。
        }
    
    }
    

    3.3 ServiceRegistryAutoConfiguration

    来自spring-cloud-commons包
    但如果要走这个函数serviceRegistryEndpoint
    必须要配置spring.jmx.enabled=true
    我没有开启它,所以这里不会走到
    至于JMX是啥,大家自己去查一下

    @Configuration(proxyBeanMethods = false)
    public class ServiceRegistryAutoConfiguration {
    
        @ConditionalOnBean(ServiceRegistry.class)
        @ConditionalOnClass(Endpoint.class)
        protected class ServiceRegistryEndpointConfiguration {
    
            @Autowired(required = false)
            private Registration registration;
    
            @Bean
            @ConditionalOnAvailableEndpoint
            public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
                ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
                endpoint.setRegistration(this.registration);
                return endpoint;
            }
    
        }
    
    }
    

    3.5 ConsulDiscoveryClientConfiguration

    在ConsulAutoConfiguration之后加载
    在下面两个类之前加载,这两个类都是spring-cloud-commons包的

    • SimpleDiscoveryClientAutoConfiguration:用来找到服务名,hostname等
    • CommonsClientAutoConfiguration:这个也是要配置spring.jmx.enabled=true

    ConsulDiscoveryClientConfiguration
    主要功能:

    • 生产ConsulDiscoveryProperties对象
    • 生产ConsulDiscoveryClient对象,用到了上面的ConsulClient
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnDiscoveryEnabled
    @ConditionalOnBlockingDiscoveryEnabled
    @ConditionalOnConsulEnabled
    @ConditionalOnConsulDiscoveryEnabled
    @EnableConfigurationProperties
    @AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
    @AutoConfigureAfter({ UtilAutoConfiguration.class, ConsulAutoConfiguration.class })
    public class ConsulDiscoveryClientConfiguration {
    
        @Bean
        @ConditionalOnMissingBean
        public ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {
            return new ConsulDiscoveryProperties(inetUtils);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public ConsulDiscoveryClient consulDiscoveryClient(ConsulClient consulClient,
                ConsulDiscoveryProperties discoveryProperties) {
            return new ConsulDiscoveryClient(consulClient, discoveryProperties);
        }
    
    }
    

    3.5 ConsulAutoServiceRegistrationAutoConfiguration

    在下面两个类之后加载

    • AutoServiceRegistrationConfiguration:来自spring-cloud-commons包,没有函数,主要是加载AutoServiceRegistrationProperties
    • ConsulServiceRegistryAutoConfiguration:3.2说明了

    主要功能:

    • 生产ConsulAutoServiceRegistration对象
    • 生产ConsulAutoServiceRegistrationListener对象:这个对象很重要是真正的启动类
    • 生产ConsulAutoRegistration对象
    • 生产ConsulRegistrationCustomizer对象:用到了ServletContext
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBean(AutoServiceRegistrationProperties.class)
    @ConditionalOnMissingBean(type = "org.springframework.cloud.consul.discovery.ConsulLifecycle")
    @ConditionalOnConsulEnabled
    @Conditional(ConsulAutoServiceRegistrationAutoConfiguration.OnConsulRegistrationEnabledCondition.class)
    @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);
        }
    
        @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);
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(ServletContext.class)
        protected static class ConsulServletConfiguration {
    
            @Bean
            public ConsulRegistrationCustomizer servletConsulCustomizer(ObjectProvider<ServletContext> servletContext) {
                return new ConsulServletRegistrationCustomizer(servletContext);
            }
    
        }
    
        protected static class OnConsulRegistrationEnabledCondition extends AllNestedConditions {
              。。。
        }
    
    }
    

    到目前为止好像什么都没发生,都是new一些对象
    但实际上我们注册了一个Listener
    ConsulAutoServiceRegistrationListener
    它实现了SmartApplicationListener接口
    SmartApplicationListener的用法请参考:使用Spring SmartApplicationListener实现业务解耦

    我在项目里用的是tomcat所以在它启动后,onApplicationEvent会被调用

    public class ConsulAutoServiceRegistrationListener implements SmartApplicationListener {
    
        private final ConsulAutoServiceRegistration autoServiceRegistration;
    
        public ConsulAutoServiceRegistrationListener(ConsulAutoServiceRegistration autoServiceRegistration) {
            this.autoServiceRegistration = autoServiceRegistration;
        }
    
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            return WebServerInitializedEvent.class.isAssignableFrom(eventType);
        }
    
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return true;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
            if (applicationEvent instanceof WebServerInitializedEvent) {
                WebServerInitializedEvent event = (WebServerInitializedEvent) applicationEvent;
    
                ApplicationContext context = event.getApplicationContext();
                if (context instanceof ConfigurableWebServerApplicationContext) {
                    if ("management".equals(((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
                        return;
                    }
                }
                this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort());
                this.autoServiceRegistration.start();
            }
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
    }
    

    接下来就是ConsulAutoServiceRegistration

    4,ConsulAutoServiceRegistration

    它的bind覆盖掉了父类的,等下看为什么

    public class ConsulAutoServiceRegistration extends AbstractAutoServiceRegistration<ConsulRegistration> { 
            。。。
          public ConsulAutoServiceRegistration(ConsulServiceRegistry serviceRegistry,
                AutoServiceRegistrationProperties autoServiceRegistrationProperties, ConsulDiscoveryProperties properties,
                ConsulAutoRegistration registration) {
            super(serviceRegistry, autoServiceRegistrationProperties);
            this.properties = properties;
            this.registration = registration;
        }
    
            @Override
        @Retryable(interceptor = "consulRetryInterceptor")
        public void start() {
            super.start();
        }
    
            @Override
        public void bind(WebServerInitializedEvent event) {
            // do nothing so we can listen for this event in a different class
            // this ensures start() can be retried if spring-retry is available
        }
    
            // 这个实现方法 会被父类调用
            @Override
        protected ConsulAutoRegistration getRegistration() {
            if (this.registration.getService().getPort() == null && this.getPort().get() > 0) {
                this.registration.initializePort(this.getPort().get());
            }
            Assert.notNull(this.registration.getService().getPort(), "service.port has not been set");
            return this.registration;
        }
            。。。
    }
    

    调用父类的start

    4.1 AbstractAutoServiceRegistration

    实现了:ApplicationListener,所以它可以触发监听事件,但它的bind被标记为过期方法了,所以上面 的子类,重写了个空的,防止start 被调用2次

    public abstract class AbstractAutoServiceRegistration<R extends Registration>
            implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    
            。。。
    
        protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
                AutoServiceRegistrationProperties properties) {
            this.serviceRegistry = serviceRegistry;
            this.properties = properties;
        }
    
        @Override
        @SuppressWarnings("deprecation")
        public void onApplicationEvent(WebServerInitializedEvent event) {
            bind(event);
        }
    
            // 这个方法被标记为过期了
        @Deprecated
        public void bind(WebServerInitializedEvent event) {
            。。。
            this.start();
        }
    
        public void start() {
            if (!isEnabled()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Discovery Lifecycle disabled. Not starting");
                }
                return;
            }
    
            // only initialize if nonSecurePort is greater than 0 and it isn't already running
            // because of containerPortInitializer below
            if (!this.running.get()) {
                            // 发布一个自定义事件InstancePreRegisteredEvent
                this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
    
                            // 无论调用 register 还是 registerManagement 最终调用的都是 ConsulServiceRegistry.register
                register();
                if (shouldRegisterManagement()) {
                    registerManagement();
                }
                            // 发布一个自定义事件InstanceRegisteredEvent
                this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
                this.running.compareAndSet(false, true);
            }
    
        }
    
            。。。
    
        protected void register() {
                    // getRegistration 为上面的子类的实现方法
            this.serviceRegistry.register(getRegistration());
        }
    
        protected void registerManagement() {
            R registration = getManagementRegistration();
            if (registration != null) {
                this.serviceRegistry.register(registration);
            }
        }
            。。。
    
    }
    

    4.2 ConsulServiceRegistry

    public class ConsulServiceRegistry implements ServiceRegistry<ConsulRegistration> {
        public ConsulServiceRegistry(ConsulClient client, ConsulDiscoveryProperties properties, TtlScheduler ttlScheduler,
                HeartbeatProperties heartbeatProperties) {
            this.client = client;
            this.properties = properties;
            this.ttlScheduler = ttlScheduler;
            this.heartbeatProperties = heartbeatProperties;
        }
    
        @Override
        public void register(ConsulRegistration reg) {
            log.info("Registering service with consul: " + reg.getService());
            try {
                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.getService());
                }
            }
            catch (ConsulException e) {
                if (this.properties.isFailFast()) {
                    log.error("Error registering service with consul: " + reg.getService(), e);
                    ReflectionUtils.rethrowRuntimeException(e);
                }
                log.warn("Failfast is false. Error registering service with consul: " + reg.getService(), e);
            }
        }
    }
    

    调用的是
    ConsulClient.agentServiceRegister

    4.3 ConsulClient

    它在com.ecwid.consul:consul-api:1.4.5 包里
    这个ConsulClient里组合了好多个Client
    包括健康检查,事件,日志等
    注册的话,是调用的agentClient

    public class ConsulClient implements {
        public ConsulClient(ConsulRawClient rawClient) {
            aclClient = new AclConsulClient(rawClient);
            agentClient = new AgentConsulClient(rawClient);
            catalogClient = new CatalogConsulClient(rawClient);
            coordinateClient = new CoordinateConsulClient(rawClient);
            eventClient = new EventConsulClient(rawClient);
            healthClient = new HealthConsulClient(rawClient);
            keyValueClient = new KeyValueConsulClient(rawClient);
            queryClient = new QueryConsulClient(rawClient);
            sessionClient = new SessionConsulClient(rawClient);
            statusClient = new StatusConsulClient(rawClient);
        }
    
        @Override
        public Response<Void> agentServiceRegister(NewService newService, String token) {
            return agentClient.agentServiceRegister(newService, token);
        }
    

    4.4 AgentConsulClient

    public final class AgentConsulClient implements AgentClient {
        private final ConsulRawClient rawClient;
    
        public AgentConsulClient(ConsulRawClient rawClient) {
            this.rawClient = rawClient;
        }
    
        @Override
        public Response<Void> agentServiceRegister(NewService newService, String token) {
            UrlParameters tokenParam = token != null ? new SingleUrlParameters("token", token) : null;
    
            String json = GsonFactory.getGson().toJson(newService);
            HttpResponse httpResponse = rawClient.makePutRequest("/v1/agent/service/register", json, tokenParam);
    
            if (httpResponse.getStatusCode() == 200) {
                return new Response<Void>(null, httpResponse);
            } else {
                throw new OperationException(httpResponse);
            }
        }
    
        @Override
        public Response<Map<String, Service>> getAgentServices() {
            HttpResponse httpResponse = rawClient.makeGetRequest("/v1/agent/services");
    
            if (httpResponse.getStatusCode() == 200) {
                Map<String, Service> agentServices = GsonFactory.getGson().fromJson(httpResponse.getContent(),
                        new TypeToken<Map<String, Service>>() {
                        }.getType());
                return new Response<Map<String, Service>>(agentServices, httpResponse);
            } else {
                throw new OperationException(httpResponse);
            }
        }
    }
    

    好的 我们看到了 HttpResponse,在这里是真正发起请求向服务器注册了

    5,总结

    Consul是在web服务启动完成后(监听WebServerInitializedEvent)向注册中心发起注册的
    如果本地服务不是个web服务,或者启动失败了。是不会注册的。
    也就保证本地服务的可用性

    相关文章

      网友评论

          本文标题:spring boot consul 客户端加载过程

          本文链接:https://www.haomeiwen.com/subject/jjhqkltx.html