SpringCloud组件内部一定会有spring-cloud-commons 和 spring-cloud-context 这两个依赖中的一个。比如 spring-cloud-netflix-eureka-server, spring-cloud-netflix-eureka-client, spring-cloud-netflix-ribbon。它们内部的这两个依赖都是optional。这些组件对应的starter内部使用了spring-cloud-starter 依赖,因为spring-cloud-starter依赖内部依赖了spring-cloud-context、spring-cloud-commons和spring-boot-starter(springboot全套架构)。
本文将分析spring-cloud-commons模块。关于spring-cloud-context将在下一篇文章中分析。
spring-cloud-commons模块是spring在分布式领域上(服务发现,服务注册,断路器,负载均衡)的规范定义(spring-cloud-netflix是具体的实现,也就是Netflix OSS里的各种组件实现了这个commons规范),可以被所有的Spring Cloud客户端使用(比如服务发现领域的eureka,consul)。下面将根据包名来分析一下内部的一些接口和类。
1、actuator功能
actuator子包里提供了一个id为 features 的 FeaturesEndpoint。该Endpoint里会展示应用具体的feature。具体的内容在HasFeatures集合属性中,HasFeatures内部包含 List 和 List 这两个属性。
1 | (id = "features") |
NamedFeature里有 String name 和 Class type 这两个属性。
abstractFeatures属性处理过程:遍历abstractFeatures集合,在ApplicationContext中找出具体Class的bean。然后根据这个具体的Class构造Feature(Feature拥有type,name,version和vendor属性)。namedFeatures属性处理过程:遍历namedFeatures集合,直接根据NamedFeature李的name和type构造Feature。
比如CommonsClientAutoConfiguration里就使用如下方式构造了一个HasFeatures。这个HasFeatures只有abstractFeatures属性有值,对应的Class是DiscoveryClient和LoadBalancerClient:
1 | (proxyBeanMethods = false) |
下面就是一个FeaturesEndpoint内容,对应DiscoveryClient和LoadBalancerClient接口,具体的type就是CompositeDiscoveryClient和RibbonLoadBalancerClient:
1 | { |
2、circuitbreaker功能
断路器功能。
circuitbreaker子包里面定义了一个注解@EnableCircuitBreaker和一个Import Selector。只要使用了该注解就会import这个selector:
1 | @Import(EnableCircuitBreakerImportSelector.class) |
selector也很简单,代码如下:
1 | (Ordered.LOWEST_PRECEDENCE - 100) |
继承了SpringFactoryImportSelector, 内部会使用工厂加载机制。去加载META-INF/spring.factories里key为 org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker的类。该机制生效的前期是 spring.cloud.circuit.breaker.enabled配置为true,默认值就是true。
circuitbreaker子包相当于了定义了断路器的加载机制。在spring.factories里配置对应的类和开关配置即可生效。具体的实现由其它模块提供。
3、discovery功能
3.1、服务发现功能
定义了DiscoveryClient接口和EnableDiscoveryClient注解。
定义了一些各种服务发现组件客户端里的读取服务操作:
1 | public interface DiscoveryClient { |
@EnableDiscoveryClient注解import了EnableDiscoveryClientImportSelector这个selector。该注解内部有个属性 boolean autoRegister() default true;表示是否自动注册,默认是true。
selector内部会找出 META-INF/spring.factories里key为org.springframework.cloud.client.discovery.EnableDiscoveryClient的类。
如果自动注册属性为true,会在找出的这些类里再加上一个类:org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration。 AutoServiceRegistrationConfiguration内部会使用@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)触发构造AutoServiceRegistrationProperties这个bean。像eureka,nacos,它们的自动化配置类里都使用了@ConditionalOnBean(AutoServiceRegistrationProperties.class)来确保存在AutoServiceRegistrationProperties这个bean存在的时候才会构造AutoServiceRegistration进行注册。
如果自动注册属性为false,在Environment里加一个PropertySource,内部的配置项是spring.cloud.service-registry.auto-registration.enabled,值是false(代表不构造AutoServiceRegistrationProperties.class)。这样eureka,nacos都不会注册
3.2、Health Indicator
springboot 中提供了一个健康检查的接口HealthIndicator, DiscoveryClient能够通过实现DiscoveryHealthIndicator来做健康检查。设置spring.cloud.discovery.client.composite-indicator.enabled=false来禁用这种混和的健康检查;DiscoveryClientHealthIndicator通常是自动配置的,设置spring.cloud.discovery.client.health-indicator.enabled=false来禁用;设置spring.cloud.discovery.client.health-indicator.include-description=false来禁用description字段,如果没有禁用,就会一直向上层传递。
3.3、Ordering DiscoveryClient instances
DiscoveryClient继承了Ordered; 当你使用多个服务发现的时候这个会很有用,可以定义通过这种方式来按照指定的顺序来从注册中心加载bean。DiscoveryClient默认的order设置的是 0 ;如果想要为你自己实现的DiscoveryClient设置不同的order,仅仅需要覆盖getOrder()。除此之外Spring Cloud还提供了配置spring.cloud.{clientIdentifier}.discovery.order来设置order,这其中主要的实现有ConsulDiscoveryClient, EurekaDiscoveryClient, ZookeeperDiscoveryClient;
3.4、discovery子包内部还有其它一些功能:
- simple
简单的服务发现实现类 SimpleDiscoveryClient,具体的服务实例从 SimpleDiscoveryProperties 配置中获取。 SimpleDiscoveryProperties 配置 读取前缀为 spring.cloud.discovery.client.simple 的配置。读取的结果放到Map里 Map。这里 SimpleServiceInstance 实现了ServiceInstance接口。 具体的属性值从 SimpleDiscoveryProperties 中获取
SimpleDiscoveryClientAutoConfiguration 自动化配置类在 spring.factories里key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置项中。内部会构造 SimpleDiscoveryProperties、 SimpleDiscoveryClient
- noop
什么都不做的服务发现实现类,已经被废弃,建议使用 simple 子模块里的类代替
- health
SpringBoot的那套health机制与SpringCloud结合。使用DiscoveryClient获取服务实例的信息
- event
定义了一些心跳检测事件,服务注册事件
- composite
定义了 CompositeDiscoveryClient。 看名字也知道,组合各个服务发现客户端的一个客户端。默认会根据CompositeDiscoveryClientAutoConfiguration自动化配置类构造出CompositeDiscoveryClient。默认清下我们注入的DiscoveryClient就是这个CompositeDiscoveryClient
4、serviceregistry功能
4.1、ServiceRegistry服务注册功能
定义了服务注册的接口 ServiceRegistry,Registration接口继承了服务实例ServiceInstance接口,未新增新方法,留作以后扩展使用。
1 | public interface ServiceRegistry<R extends Registration> { |
比如要取消自动注册,改为手动注册,示例代码如下:
1 |
|
每个ServiceRegistry实现类都会提供一个对应的服务注册实现
ZookeeperRegistration使用的ZookeeperServiceRegistryEurekaRegistration使用的EurekaServiceRegistryConsulRegistration使用的ConsulServiceRegistry
4.2、ServiceRegistry Auto-Registration
4.2.1、禁用自动注册服务功能
默认情况下ServiceRegistry的实现类在运行的时候会自动注册服务,两种方式来禁用自动注册服务
1 | # 通过注解方式禁用自动注册服务 |
当一个服务自动注册的时会触发两个事件:
- 第一个是
InstancePreRegisteredEvent,在注册之前触发; - 第二个是
InstanceRegisteredEvent在注册完成之后触发;可以使用ApplicationListener来监听这两个事件
当spring.cloud.service-registry.auto-registration.enabled=false的时候就不会触发这两个事件
4.2.2、原理剖析
spring-cloud-commons项目的serviceresgistry包中定义了一些自动化配置类:
ServiceRegistryAutoConfiguration
内部会根据条件注解判断是否构造ServiceRegistryEndpoint,该endpoint会暴露ServiceRegistry的状态信息,也可以设置ServiceRegistry的状态信息。Spring Cloud Commons 提供了一个
/service-registry端点,这个endpoint依赖于容器中的Registration。GET请求这个地址将会返回Registration的状态;POST请求这个地址可以修改Registration,这个json格式的body中必须要包含一个status;查询ServiceRegistry的实现类文档来确定status的值;比如Eureka的状态值:UP, DOWN, OUT_OF_SERVICE, UNKNOWN.AutoServiceRegistrationAutoConfiguration
在@EnableDiscoveryClient注解打开自注册开关的时候才会生效,内部import了AutoServiceRegistrationConfiguration这个类,该类内部会使用@EnableConfigurationProperties注解构造AutoServiceRegistrationProperties这个bean
- 定义了一个接口AutoServiceRegistration 和一个抽象类
AbstractAutoServiceRegistration,用于处理服务自动注册逻辑。一般我们自定义的服务注册逻辑只需要继承该类即可。
AutoServiceRegistration接口无任何方法声明,用于标记是否是服务自动注册。
AbstractAutoServiceRegistration 抽象类实现了AutoServiceRegistration接口,定义了4个抽象方法:
1 | // 服务注册信息的配置数据 |
AbstractAutoServiceRegistration 抽象类内部逻辑总结:
构造方法里必须有个
ServiceRegistry参数,服务注册相关的逻辑都使用该接口完成监听
WebServerInitializedEvent事件。
当WebServer初始化完毕后(Spring ApplicationContext也已经refresh后),使用ServiceRegistry注册服务,具体的服务信息在抽象方法getRegistration()里由子类实现。当子类实现的getManagementRegistration()接口有返回具体的注册信息并且配置的management信息后注册这个management信息该类销毁的时候使用
ServiceRegistry下线服务(下线过程跟注册过程雷同,下线getRegistration()和getManagementRegistration()方法里返回的注册信息),并调用ServiceRegistry的close方法关闭注册器。
总结一下,在SpringCloud体系下要实现新的服务注册、发现需要这6个步骤(最新版本的spring-cloud-commons已经建议我们直接使用Registration,废弃ServiceInstance):
- 实现
ServiceRegistry接口,完成服务注册自身的具体逻辑 - 实现
Registration接口,完成服务注册过程中获取注册信息的操作 - 继承
AbstractAutoServiceRegistration,完成服务注册前后的逻辑 - 实现
DiscoveryClient接口,完成服务发现的具体逻辑 - 实现
ServiceInstance接口,在DiscoveryClient接口中被使用,完成服务注册组件与SpringCloud注册信息的转换
自动化配置类,将这些Bean进行构造
5、loadbalancer功能
5.1、使用示例
创建一个支持负载均衡的RestTemplate,使用@LoadBalanced和@Bean注解,像下面的例子:
1 | @Configuration |
5.2、客户端负载均衡功能
一些接口的定义:
ServiceInstanceChooser:服务实例选择器,使用load balancer根据serviceId获取具体的实例。
1 | public interface ServiceInstanceChooser { |
LoadBalancerClient:负载均衡客户端,继承ServiceInstanceChooser。
1 | public interface LoadBalancerClient extends ServiceInstanceChooser { |
RestTemplateCustomizer:RestTemplate的定制化器。
1 | public interface RestTemplateCustomizer { |
LoadBalancerRequestTransformer:HttpRequest转换器,根据ServiceInstance转换成一个新的具有load balance功能的HttpRequest。
1 | @Order(LoadBalancerRequestTransformer.DEFAULT_ORDER) |
LoadBalancerRequest:函数式接口。对ServiceInstance操作并返回具体的泛型T。LoadBalancerRequestFactory的createRequest方法内部实现了该接口。实现过程中使用LoadBalancerRequestTransformer对request进行转换并返回了ClientHttpResponse。
1 | public interface LoadBalancerRequest |
@LoadBalanced注解用于修饰RestTemplate,表示使用负载均衡客户端。
使用该注解修饰的RestTemplate会在LoadBalancerAutoConfiguration自动化配置类中被处理:
1 | // 使用RestTemplateCustomizer定制化这些被@LoadBalanced注解修饰的RestTemplate |
LoadBalancerInterceptor拦截器内部会对request请求进行拦截。拦截器内部使用LoadBalancerClient完成请求的调用,这里调用的时候需要的LoadBalancerRequest由LoadBalancerRequestFactory构造,LoadBalancerRequestFactory内部使用LoadBalancerRequestTransformer对request进行转换。
5.3 Retrying Failed Requests
RestTemplate可以配置请求失败后的重试策略;默认这个逻辑是禁止的,如果需要可以开启,只需要添加 Spring Retry到classpath; 如果spring retry已经在classpath,你想要禁用这个retry的功能,那么可以配置spring.cloud.loadbalancer.retry.enabled=false
如果想要自定义一个BackOffPolicy,需要创建一个LoadBalancedRetryFactory并覆写方法createBackOffPolicy; eg:
1 | @Configuration |
5.4 Multiple RestTemplate objects
如何创建一个支持负载均衡的RestTemplate和不支持负载均衡的RestTemplate以及注入的方式?看下面的列子:
1 | @Configuration |
@Primary的作用是在使用@Autowired注入时,如果发现了多个类型的bean, 就选择使用了@Primary的bean
如果遇到了这个异常java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89,可以尝试注入类型修改RestOperations或者设置spring.aop.proxyTargetClass=true
6、hypermedia功能
springcloud对hateoas在服务发现领域上的支持。
关于hateoas可以参考一些资料:
https://github.com/spring-projects/spring-hateoas
https://spring.io/guides/gs/rest-hateoas/
其它注解、接口、类
- @SpringCloudApplication注解
整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker这3个注解,说明@SpringCloudApplication注解表示一个分布式应用注解
ServiceInstance接口
表示服务发现系统里的一个服务实例
DefaultServiceInstance类
实现了ServiceInstance接口,是个默认的服务实例的实现
HostInfoEnvironmentPostProcessor类
属于EnvironmentPostProcessor。这个postprocessor会在`Environment里加上当前vm的hostname和ip信息
CommonsClientAutoConfiguration类
自动化配置类。在该模块的 META-INF/spring.factories里配置,key为org.springframework.boot.autoconfigure.EnableAutoConfiguration。所以默认会被加载,内部会构造一些HealthIndicator,一些Endpoint
Spring Cloud Alibaba内部的spring-cloud-alibaba-nacos-discovery模块实现了spring-cloud-commons规范,提供了基于Nacos的服务发现,服务注册功能。Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。


