目录
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).。
Spring Cloud Context
通过spring boot 构建 spring application已经很方便了,她还提供了一些监控、管理相关的endpoint; 由于 spring cloud 是建立在spring boot基础之上的,所以spring boot 的功能spring cloud都满足,此外还提供了一些特有的特性,这些特性可能在所有的spring cloud组件中都会(或者偶尔)使用到。
Bootstrap Context
spring cloud 应用中创建一个 bootstrap context,她是作为了主应用的 parent context ;主要负责从外部资源(主要指配置中心)加载配置以及再解码本地的配置文件。appliction context 和 bootstrap context 共享了相同Environment. 默认情况下,bootstrap properties(从配置中心加载的属性) 有更高的优先级,所以它们不能被本地的配置所覆盖。
bootstrap context使用了和主应用的 context 不同的配置方式,使用的是 bootstrap.yml 而不是 application.yml,这主要是为了保证两个context的配置可以很好的分离。如果需要禁用 bootstrap context ,我们可以设置环境变量spring.cloud.bootstrap.enabled=false
Application Context 层级
如果你通过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是所有容器的父容器。
改变本地Bootstrap属性
能够通过设置环境变量spring.cloud.bootstrap.name(默认是bootstrap)配置引导文件(bootstrap.yml)的名字,通过 spring.cloud.bootstrap.location(默认是空)配置路径。
例如在环境变量中配置:
spring.cloud.bootstrap.name=start
spring.cloud.bootstrap.location=classpath:/test/
覆盖远程属性值
通过bootstrap context从远程添加的属性默认情况下是不能被本地配置所覆盖的。如果你想要使用系统变量或者配置文件来覆盖远程的属性,在远程配置中设置属性spring.cloud.config.allowOverride=true(注意:在本地文件中设置是无效的)来授权,同时需要配置哪些可以覆盖:
spring.cloud.config.overrideNone=true # 本地任何属性都可以覆盖
spring.cloud.config.overrideSystemProperties=false #环境变量,命令行参数可以覆盖,本地配置文件除外
自定义boostrap配置
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。
自定义boostrap PropertySource
默认情况下外部化配置都是从配置中心获取属性,但是也可以通过实现PropertySourceLocator来实现添加属性到bootstrap context(需要把实现类添加到spring.factories);比如可以从不同的数据库或者服务器获取配置。
Environment 改变
应用可以监听EnvironmentChangeEvent来响应环境变量被改变的事件。一旦EnvironmentChangeEvent事件被触发,应用程序将会做这些事情:
- 重新绑定使用了@ConfigurationProperties的类
- 根据logging.level.*来设置应用的日志级别
默认情况下,Config Client不轮询Environment的改变。一般情况,不建议使用这种方式来监测变化(虽然你可以通过@Scheduled注解来设置)。对于可扩展的应用程序,使用广播EnvironmentChangeEvent到所有实例的方式,好过轮询的方式。(比如使用Spring Cloud Bus项目)。
Refresh Scope
当有配置发生变化的时候,使用了@RefreshScope的类将会被处理;这个特性解决了有状态bean只能在初始化注入配置的问题。例如:比如,在使用DataSource获得一个数据库连接的是时候,当通过Environment修改数据库连接字符串的时候,我们可以通过执行@RefreshScope来根据修改的配置获取一个新的URL的连接。
Refresh scope beans是延迟初始化的,在第一次使用的时候才初始化,这个scope充当了初始值的缓存;为了在下一次调用时强制初始化,必须使缓存无效。
RefreshScope在容器中是一个bean, 提供了一个public方法refreshAll(),通过清理当前的缓存来刷新,可以通过访问/refresh来触发刷新;也可以使用bean的名字来刷新refresh(String),要使用还需要暴露出这个endpoint
Endpoints
相对于Spring Boot Actuator的应用,添加了一些管理端点:
- POST /actuator/env :更新Environment重新加载@ConfigurationProperties和日志级别
- /actuator/refresh: 重新初始化添加了@RefreshScope 的bean
- /actuator/restart: 重启初始化ApplicationContext,重启 (默认是禁用的)
- /actuator/pause: 调用ApplicationContext生命周期的方法stop()和start()
如果禁用了/actuator/restart,那么/actuator/pause和/actuator/resume都会被禁用
Spring Cloud Commons
服务发现、负载平衡和断路器等通用的模型,本身是一个抽象层,可以被所有Spring Cloud组件独立的实现,例如服务发现有具体的实现Eureka、Consul。
@EnableDiscoveryClient
Spring Cloud Commons 提供了@EnableDiscoveryClient这个注解,主要的作用是在META-INF/spring.factories寻找DiscoveryClient的实现类;实现了DiscoveryClient的类都会配置在spring.factories中,添加key为 org.springframework.cloud.client.discovery.EnableDiscoveryClient
@EnableDiscoveryClient现在已经不是必须的了,只要DiscoveryClient的实现类在classpath就OK
健康指示器
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字段,如果没有禁用,就会一直向上层传递。
服务发现实例排序
DiscoveryClient继承了Ordered; 当你使用多个服务发现的时候这个会很有用,可以定义通过这种方式来按照指定的顺序来从注册中心加载bean。DiscoveryClient默认的order设置的是 0 ;如果想要为你自己实现的DiscoveryClient设置不同的order,仅仅需要覆盖getOrder()。除此之外Spring Cloud提供了配置spring.cloud.{clientIdentifier}.discovery.order来设置order,这其中主要是ConsulDiscoveryClient, EurekaDiscoveryClient, ZookeeperDiscoveryClient;
服务注册
ServiceRegistry接口提供了注册服务register(Registration) 和 取消注册deregister(Registration)的方法。
@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
自动注册
默认情况下ServiceRegistry的实现类在运行的时候会自动注册服务,两种方式来禁用自动注册服务
@EnableDiscoveryClient(autoRegister=false)
spring.cloud.service-registry.auto-registration.enabled=false
当一个服务自动注册的时会触发两个事件,第一个是InstancePreRegisteredEvent,在注册之前触发;第二个是InstanceRegisteredEvent 在注册完成之后触发;可以使用ApplicationListener来监听这两个事件。
当
spring.cloud.service-registry.auto-registration.enabled=false
的时候就不会触发这两个事件
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.
负载均衡
创建一个支持负载均衡的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;
}
}
失败请求重试
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();
}
};
}
}
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