spring cloud 中的许多特性都已经被spring boot实现,其他一些特性主要是通过spring cloud context 和 spring cloud commons来实现的。spring cloud context提供了一些常用的公共类以及spring cloud 特有的服务(bootstrap context, encryption, refresh scope, and environment endpoints);spring cloud commons提供了一系列的抽象类和公共类,供spring cloud不同的实现组件去使用(such as Spring Cloud Netflix and Spring Cloud Consul).。
1. Spring Cloud Context: Application Context Services
通过spring boot 构建 spring application已经很方便了,她还提供了一些监控、管理相关的endpoint; 由于 spring cloud 是建立在spring boot基础之上的,所以spring boot 的功能spring cloud都满足,此外还提供了一些特有的特性,这些特性可能在所有的spring cloud组件中都会(或者偶尔)使用到
1.1 The Bootstrap Application Context
spring cloud 应用中创建一个 bootstrap context
,她是作为了主应用的 parent context
;主要负责从外部资源(主要指配置中心)加载配置以及在解码本地的配置文件。appliction context
和 bootstrap context
共享了相同Environment
. 默认情况下,bootstrap properties
(从配置中心加载的属性) 有更高的优先级,所以它们不能被本地的配置所覆盖。
bootstrap context
使用了和主应用的 context
不同的配置方式,使用的是 bootstrap.yml
而不是 application.yml
,这主要是为了保证两个context的配置可以很好的分离;下面是 bootstrap.yml
的例子:
spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果需要禁用 bootstrap context
,我们可以设置环境变量spring.cloud.bootstrap.enabled=false
1.2 Application Context Hierarchies
如果你通过SpringApplication
或者 SpringApplicationBuilder
来构建你的应用,那么Bootstrap context
将会作为application context
的 parent context
。子容器会继承从父容器中的属性,新增的属性有:
-
bootstrap
: 如果在Bootstrap context
有任何非空的属性,那么这些属性的优先级都更高,比如说从 spring cloud config 加载的配置,默认优先级就比本地配置高;后面会提到如何覆盖这些属性 -
applicationConfig
: 如果你配置bootstrap.yml
, 那么bootstrap.yml
中的属性会用来配置Bootstrap context
;当Bootstrap context
配置完成后就会把这些属性添加到application context
中,这些属性的优先级会比配置在application.yml
中的低
由于bootstrap.yml
的优先级比较低,所以可以用来设置一些属性的默认值
spring cloud 允许你扩展ApplicationContext
,例如可以使用 ApplicationContext
已有的接口或者使用SpringApplicationBuilder
提供的方法(parent(), child() and sibling());这个bootstrap context
是所有容器的父容器
1.3 Changing the Location of Bootstrap Properties
能够通过设置环境变量spring.cloud.bootstrap.name
(默认是bootstrap)配置引导文件(bootstrap.yml)的名字,通过 spring.cloud.bootstrap.location
(默认是空)配置路径。
例如在环境变量中配置:
spring.cloud.bootstrap.name=start
spring.cloud.bootstrap.location=classpath:/test/
1.4 Overriding the Values of Remote Properties
通过bootstrap context
从远程添加的属性默认情况下是不能被本地配置所覆盖的。如果你想要使用系统变量或者配置文件来覆盖远程的属性,在远程配置中设置属性spring.cloud.config.allowOverride=true
(注意:在本地文件中设置是无效的)来授权,同时需要配置哪些可以覆盖:
-
spring.cloud.config.overrideNone=true
本地任何属性都可以覆盖 -
spring.cloud.config.overrideSystemProperties=false
环境变量,命令行参数可以覆盖,本地配置文件除外
1.5 Customizing the Bootstrap Configuration
bootstrap context
通过/META-INF/spring.factories
配置的类初始化的所有的Bean都会在SpingApplicatin启动前加入到它的上下文里去。spring.factories
中key 使用 org.springframework.cloud.bootstrap.BootstrapConfiguration
, 所有需要用来配置bootstrap context
的配置类用逗号分隔;可以通过@Order来更改初始化序列,默认是”last”
首先使用spring.factories
中配置的类来创建bootstrap context
,然后所有使用@Bean
注解的ApplicationContextInitializer
将会被添加 main application context
1.6 Customizing the Bootstrap Property Sources
默认情况下外部化配置都是从配置中心获取属性,但是也可以通过实现PropertySourceLocator
来实现添加属性到bootstrap context
(需要把实现类添加到spring.factories);比如可以从不同的数据库或者服务器获取配置。
@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
如果你把这个类打包到了一个jar里面,添加下面的配置到META-INF/spring.factories
里面,那么所有依赖了这个jar的都会有customProperty
这个配置
org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
1.8 Environment Changes
应用可以监听EnvironmentChangeEvent
来响应环境变量被改变的事件。一旦EnvironmentChangeEvent
事件被触发,应用程序将会做这些事情:
- 重新绑定使用了
@ConfigurationProperties
的类 - 根据
logging.level.*
来设置应用的日志级别
默认情况下,Config Client不轮询Environment的改变。一般情况,不建议使用这种方式来监测变化(虽然你可以通过@Scheduled注解来设置)。对于可扩展的应用程序,使用广播EnvironmentChangeEvent到所有实例的方式,好过轮询的方式。(比如使用Spring Cloud Bus项目)。
1.9 Refresh Scope
当有配置发生变化的时候,使用了@RefreshScope
的类将会被处理;这个特性解决了有状态bean只能在初始化注入配置的问题。例如:比如,在使用DataSource获得一个数据库连接的是时候,当通过Environment
修改数据库连接字符串的时候,我们可以通过执行@RefreshScope
来根据修改的配置获取一个新的URL的连接。
Refresh scope beans是延迟初始化的,在第一次使用的时候才初始化,这个scope充当了初始值的缓存;为了在下一次调用时强制初始化,必须使缓存无效。
RefreshScope
在容器中是一个bean, 提供了一个public方法refreshAll()
,通过清理当前的缓存来刷新,可以通过访问/refresh
来触发刷新;也可以使用bean的名字来刷新refresh(String)
,要使用还需要暴露出这个endpoint
management:
endpoints:
web:
exposure:
include: refresh
注意:
@RefreshScope
注解在一个@Configuration
类上面,在重新初始化的时候需要注意到可以相互依赖造成的冲突。
1.11 Endpoints
相对于Spring Boot Actuator的应用,添加了一些管理端点:
- POST to
/actuator/env
更新Environment
重新加载@ConfigurationProperties
和日志级别 -
/actuator/refresh
重新初始化添加了@RefreshScope
的bean -
/actuator/restart
重启初始化ApplicationContext
,重启 (默认是禁用的) -
/actuator/pause
调用ApplicationContext
生命周期的方法stop()
和start()
如果禁用了
/actuator/restart
,那么/actuator/pause
和/actuator/resume
都会被禁用
2. Spring Cloud Commons: Common Abstractions
服务发现、负载平衡和断路器等通用的模型,本身是一个抽象层,可以被所有Spring Cloud组件独立的实现,例如服务发现有具体的实现Eureka、Consul。
2.1 @EnableDiscoveryClient
Spring Cloud Commons 提供了@EnableDiscoveryClient
这个注解,主要的作用是在META-INF/spring.factories
寻找DiscoveryClient
的实现类;实现了DiscoveryClient
的类都会配置在spring.factories
中,添加key为 org.springframework.cloud.client.discovery.EnableDiscoveryClient
@EnableDiscoveryClient
现在已经不是必须的了,只要DiscoveryClient
的实现类在classpath就OK
2.1.1 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字段,如果没有禁用,就会一直向上层传递。
2.1.2 Ordering DiscoveryClient instances
DiscoveryClient
继承了Ordered
; 当你使用多个服务发现的时候这个会很有用,可以定义通过这种方式来按照指定的顺序来从注册中心加载bean。DiscoveryClient
默认的order设置的是 0 ;如果想要为你自己实现的DiscoveryClient
设置不同的order,仅仅需要覆盖getOrder()
。除此之外Spring Cloud提供了配置spring.cloud.{clientIdentifier}.discovery.order
来设置order,这其中主要是ConsulDiscoveryClient
, EurekaDiscoveryClient
, ZookeeperDiscoveryClient
;
2.2 ServiceRegistry
ServiceRegistry
接口提供了注册服务register(Registration)
和 取消注册deregister(Registration)
的方法
eg:
@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
private ServiceRegistry registry;
public MyConfiguration(ServiceRegistry registry) {
this.registry = registry;
}
// called through some external process, such as an event or a custom actuator endpoint
public void register() {
Registration registration = constructRegistration();
this.registry.register(registration);
}
}
每个ServiceRegistry
实现类都会提供一个对应Registry
- ZookeeperRegistration 使用的 ZookeeperServiceRegistry
- EurekaRegistration 使用的 EurekaServiceRegistry
- ConsulRegistration 使用的 ConsulServiceRegistry
2.2.1 ServiceRegistry Auto-Registration
默认情况下ServiceRegistry
的实现类在运行的时候会自动注册服务,两种方式来禁用自动注册服务
- @EnableDiscoveryClient(autoRegister=false)
- spring.cloud.service-registry.auto-registration.enabled=false
当一个服务自动注册的时会触发两个事件,第一个是InstancePreRegisteredEvent
,在注册之前触发;第二个是InstanceRegisteredEvent
在注册完成之后触发;可以使用ApplicationListener
来监听这两个事件
当
spring.cloud.service-registry.auto-registration.enabled=false
的时候就不会触发这两个事件
2.2.2 Service Registry Actuator Endpoint
Spring Cloud Commons 提供了一个/service-registry
端点,这个endpoint依赖于容器中的Registration
。GET请求这个地址将会返回Registration
的状态;POST请求这个地址可以修改Registration
,这个json格式的body中必须要包含一个status
;查询ServiceRegistry
的实现类文档来确定status的值;比如Eureka的状态值:UP
, DOWN
, OUT_OF_SERVICE
, UNKNOWN
.
2.3 Spring RestTemplate as a Load Balancer Client
创建一个支持负载均衡的RestTemplate
,使用@LoadBalanced
和@Bean
注解,像下面的例子:
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
public String doOtherStuff() {
String results = restTemplate.getForObject("http://stores/stores", String.class);
return results;
}
}
2.4 Retrying Failed Requests
RestTemplate
可以配置请求失败后的重试策略;默认这个逻辑是禁止的,如果需要可以开启,只需要添加 Spring Retry到classpath; 如果spring retry已经在classpath,你想要禁用这个retry的功能,那么可以配置spring.cloud.loadbalancer.retry.enabled=false
如果想要自定义一个BackOffPolicy
,需要创建一个LoadBalancedRetryFactory
并覆写方法createBackOffPolicy
; eg:
@Configuration
public class MyConfiguration {
@Bean
LoadBalancedRetryFactory retryFactory() {
return new LoadBalancedRetryFactory() {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return new ExponentialBackOffPolicy();
}
};
}
}
2.5 Multiple RestTemplate objects
如何创建一个支持负载均衡的RestTemplate
和不支持负载均衡的RestTemplate
以及注入的方式?看下面的列子:
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
@Primary
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
@Autowired
@LoadBalanced
private RestTemplate loadBalanced;
public String doOtherStuff() {
return loadBalanced.getForObject("http://stores/stores", String.class);
}
public String doStuff() {
return restTemplate.getForObject("http://example.com", String.class);
}
}
@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