spring-cloud-commons reference

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 contextbootstrap 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 contextparent 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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容