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 | "features") (id = |
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 | false) (proxyBeanMethods = |
下面就是一个FeaturesEndpoint
内容,对应DiscoveryClient
和LoadBalancerClient
接口,具体的type就是CompositeDiscoveryClient
和RibbonLoadBalancerClient
:
1 | { |
2、circuitbreaker功能
断路器功能。
circuitbreaker
子包里面定义了一个注解@EnableCircuitBreaker
和一个Import Selector。只要使用了该注解就会import这个selector:
1 | @Import(EnableCircuitBreakerImportSelector.class) |
selector也很简单,代码如下:
1 | 100) (Ordered.LOWEST_PRECEDENCE - |
继承了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
使用的ZookeeperServiceRegistry
EurekaRegistration
使用的EurekaServiceRegistry
ConsulRegistration
使用的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是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。