spring boot consul 客户端加载过程

2021-04-08  本文已影响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

主要功能:

@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:它的主要功能

ConsulServiceRegistryAutoConfiguration
主要功能:

@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包的

ConsulDiscoveryClientConfiguration
主要功能:

@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

在下面两个类之后加载

主要功能:

@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服务,或者启动失败了。是不会注册的。
也就保证本地服务的可用性

上一篇下一篇

猜你喜欢

热点阅读