一、工程结构
二、UML
- Registration:一个标记接口,继承了ServiceInstance代表一个抽象的服务实例,提供serviceId、host、port、URI、协议、metadata等getter、setter方法。consul对于他的实现就是ConsulRegistration。而ConsulAutoRegistration是Consul自己对于服务注册的扩展,Consul自己的服务实例就是NewService,它Consul自己的metadata、健康检查Check参数设定等等。
- ServiceRegistry:对于Regisration,提供服务注册、服务注销、服务状态的getter、setter方法。ConsulServiceRegistry是他的实现。
- AutoServiceRegistration:一个标记接口,它的抽象实现是AbstractAutoServiceRegistration,具体实现是ConsulAutoServiceRegistration。AbstractAutoServiceRegistration类是由spring-cloud-commons提供的。它的start方法提供了服务自动注册的能力,具体如何获取配置、Registration实例由子类实现。
- ConsulAutoServiceRegistrationListener:实现SmartApplicationListener,利用Spring事件机制的扩展点,触发服务自动注册
- ConsulRegistrationCustomizer:Consul为ConsulRegistration提供的扩展点。在ConsulRegistration构造完成之后,提供给用户一个扩展ConsulRegistration的接口
public interface ConsulRegistrationCustomizer {
void customize(ConsulRegistration registration);
}
- ConsulServiceRegistryAutoConfiguration:ConsulServiceRegistry的配置类
- ConsulAutoServiceRegistrationAutoConfiguration:ConsulAutoRegistration和ConsulServiceAutoRegistration的配置类
三、从配置讲起
- spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration,\
org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration\
...
3.1 ConsulServiceRegistryAutoConfiguration
- 注入条件
// 1、spring.cloud.consul.enabled matchIfMissing = true
// 2、ConsulClient.class存在
@ConditionalOnConsulEnabled
// 1、spring.cloud.service-registry.enabled matchIfMissing = true
// 2、spring.cloud.consul.service-registry.enabled matchIfMissing = true
@Conditional(ConsulServiceRegistryAutoConfiguration.OnConsulRegistrationEnabledCondition.class)
@AutoConfigureBefore(ServiceRegistryAutoConfiguration.class)
public class ConsulServiceRegistryAutoConfiguration {
- ServiceRegistryAutoConfiguration:spring-cloud-common提供,对服务注册的健康检查
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;
}
}
}
- ConsulServiceRegistry:直接操作ConsulClient,提供服务注册功能
@Bean
@ConditionalOnMissingBean
public ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient,
ConsulDiscoveryProperties properties, HeartbeatProperties heartbeatProperties,
@Autowired(required = false) TtlScheduler ttlScheduler) {
return new ConsulServiceRegistry(consulClient, properties, ttlScheduler,
heartbeatProperties);
}
- HeartbeatProperties:健康检查的心跳检测方式的配置类
@Bean
@ConditionalOnMissingBean
public HeartbeatProperties heartbeatProperties() {
return new HeartbeatProperties();
}
- ConsulDiscoveryProperties:服务注册与发现的相关配置(服务注册和发现的配置混合在一起,官方注释的意思是考虑把注册和发现的配置分开管理更合适)
@Bean
@ConditionalOnMissingBean
// TODO: Split appropriate values to service-registry for Edgware
public ConsulDiscoveryProperties consulDiscoveryProperties(InetUtils inetUtils) {
return new ConsulDiscoveryProperties(inetUtils);
}
3.2 ConsulAutoServiceRegistrationAutoConfiguration
- 生效条件
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnMissingBean(
type = "org.springframework.cloud.consul.discovery.ConsulLifecycle")
// 1、spring.cloud.consul.enabled matchIfMissing = true
// 2、ConsulClient.class存在
@ConditionalOnConsulEnabled
// 1、spring.cloud.service-registry.auto-registration.enabled matchIfMissing = true
// 2、spring.cloud.consul.service-registry.auto-registration.enabled matchIfMissing = true
// 3、spring.cloud.service-registry.enabled matchIfMissing = true
// 4、spring.cloud.consul.service-registry.enabled matchIfMissing = true
@Conditional(ConsulAutoServiceRegistrationAutoConfiguration.OnConsulRegistrationEnabledCondition.class)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
ConsulServiceRegistryAutoConfiguration.class })
public class ConsulAutoServiceRegistrationAutoConfiguration {
- ConsulAutoRegistration:代表当前服务实例
@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);
}
- ConsulAutoServiceRegistration:实现AbstractAutoServiceRegistration,提供服务注册功能
@Bean
@ConditionalOnMissingBean
public ConsulAutoServiceRegistration consulAutoServiceRegistration(
ConsulServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties,
ConsulAutoRegistration consulRegistration) {
return new ConsulAutoServiceRegistration(registry,
autoServiceRegistrationProperties, properties, consulRegistration);
}
- ConsulAutoServiceRegistrationListener:服务自动注册的入口
@Bean
public ConsulAutoServiceRegistrationListener consulAutoServiceRegistrationListener(
ConsulAutoServiceRegistration registration) {
return new ConsulAutoServiceRegistrationListener(registration);
}
四、ConsulAutoServiceRegistrationListener何时执行服务注册,通过谁操作服务注册
- ConsulAutoServiceRegistrationListener作为服务注册的入口,利用了Spring的哪个扩展点?
通过debug发现,ConsulAutoServiceRegistrationListener关注的Event是在Servlet容器启动后触发的,触发入口是:org.springframework.boot.web.reactive.context.WebServerManager#start
void start() {
this.handler.initializeHandler();
// Tomcat启动
this.webServer.start();
// 发布ReactiveWebServerInitializedEvent事件,这个事件继承了WebServerInitializedEvent
this.applicationContext.publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
}
而ConsulAutoServiceRegistrationListener监听的事件正是WebServerInitializedEvent,从而触发了服务注册流程。
public class ConsulAutoServiceRegistrationListener implements SmartApplicationListener {
// 监听WebServerInitializedEvent事件
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return WebServerInitializedEvent.class.isAssignableFrom(eventType);
}
- 通过谁操作服务注册?
ConsulAutoServiceRegistration实现了AbstractAutoServiceRegistration,主要是提供了服务注册流程上的具体实现,而具体操作Consul服务注册的API那是ConsulServiceRegistry的事情。
public class ConsulAutoServiceRegistrationListener implements SmartApplicationListener {
private final ConsulAutoServiceRegistration autoServiceRegistration;
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent instanceof WebServerInitializedEvent) {
WebServerInitializedEvent event = (WebServerInitializedEvent) applicationEvent;
// 通过WebServerInitializedEvent可以获取到WebServer实例,进而获取到启动端口
this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort());
// 执行服务注册流程
this.autoServiceRegistration.start();
}
}
}
五、服务注册流程:ConsulAutoServiceRegistration
- ConsulAutoServiceRegistration的start方法仅仅是调用了抽象父类的start方法,抽象父类的start方法属于一个模板方法,很多东西是靠子类进行实现的
- 同时注意到之前提到的core模块提供的springRetry拦截器在这里使用了,如果我们引入spring-retry框架就可以按照配置条件执行服务注册的重试了。juejin.cn/post/687154…
@Override
@Retryable(interceptor = "consulRetryInterceptor")
public void start() {
// 1. 判断ConsulAutoServiceRegistration.isEnabled
// 2. 发布InstancePreRegisteredEvent事件
// 3. 调用ConsulAutoServiceRegistration.register
// 4. 发布InstanceRegisteredEvent事件
// 5. 设置状态running=true
super.start();
}
- AbstractAutoServiceRegistration.start:主要控制了服务注册的流程,把扩展点都预留出来,不用让子类考虑这些事情,子类只要负责具体服务注册的事情
public void start() {
// 1. 子类实现:判断ConsulAutoServiceRegistration.isEnabled
if (!isEnabled()) {
return;
}
if (!this.running.get()) {
// 2. 抽象类控制,发布InstancePreRegisteredEvent事件
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
// 3. 子类实现如何注册一个服务:调用ConsulAutoServiceRegistration.register
register();
// 4. 抽象类控制,发布InstanceRegisteredEvent事件
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
// 5. 抽象类控制,设置已经执行过服务注册了
this.running.compareAndSet(false, true);
}
}
- 具体看一下ConsulAutoServiceRegistration.register的逻辑,如何实现服务注册
protected void register() {
// 1. 判断spring.cloud.consul.discovery.register
if (!this.properties.isRegister()) {
log.debug("Registration disabled.");
return;
}
// 2. 调用ConsulServiceRegistry注册 自动注入的ConsulAutoRegistration实例
super.register();
}
- 子类仅仅是判断了一下配置是否允许辅助注册,最终实现还是父类AbstractAutoServiceRegistration的register方法,而父类的实现,也仅仅是用注入的ConsulServiceRegistry执行register方法。其中注册的Registration服务实例,是注入的ConsulAutoRegistration(这个东西放到Consul健康检查的时候再讲,目前就当做一个服务实例,包含host、port等必要信息)。
protected void register() {
this.serviceRegistry.register(getRegistration());
}
- ConsulServiceRegistry.register,主要负责操作ConsulAPI真正执行服务注册
@Override
public void register(ConsulRegistration reg) {
try {
// 1. http://localhost:8500/v1/agent/service/register 向consul注册服务实例
// 报文内容
// {"ID":"testConsulApp-8080","Name":"testConsulApp","Tags":["secure\u003dfalse"],"Address":"192.168.0.105","Meta":{},"Port":8080,"Check":{"Interval":"10s","HTTP":"http://192.168.0.105:8080/actuator/health","Header":{}}}
this.client.agentServiceRegister(reg.getService(),
this.properties.getAclToken());
NewService service = reg.getService();
// 2. 如果健康检查的方式是心跳方式,则提交一个定时任务,持续向consul agent发送心跳
if (this.heartbeatProperties.isEnabled() && this.ttlScheduler != null
&& service.getCheck() != null
&& service.getCheck().getTtl() != null) {
this.ttlScheduler.add(reg.getInstanceId());
}
} catch (ConsulException e) {
// 3. 如果是快速失败,直接把异常抛出,否则吃掉异常
if (this.properties.isFailFast()) {
ReflectionUtils.rethrowRuntimeException(e);
}
}
}
六、总结
- 主要理解Registration、ServiceRegistry等的作用职责是什么
- SpringCloudConsul服务注册利用了Spring容器事件这个扩展点,执行了服务注册
- SpringCloudConsul服务注册的时机是Servlet容器启动之后,发布WebServerInitializedEvent事件,并且通过这个事件能拿到例如Tomcat等容器实例,从而获得类似端口等属性
- AbstractAutoServiceRegistration控制服务注册流程,预留扩展点,让子类ConsulAutoServiceRegistration专心实现服务注册
- /v1/agent/service/register是consul服务注册的api