Spring Cloud Native Application 原生应用
原生应用:是一种程序开发风格,是连续交付(持续集成)和值驱动领域的最佳实践。
12-factors
与12-Factor (SaaS)应用,有着类似的目标: (12-Factor 为构建如下的 SaaS 应用提供了方法论):
- 使用标准化流程自动配置,从而使新的开发者花费最少的学习成本加入这个项目。
- 和操作系统之间尽可能的划清界限,在各个系统中提供最大的可移植性。
- 适合部署在现代的云计算平台,从而在服务器和系统管理方面节省资源。
- 将开发环境和生产环境的差异降至最低,并使用持续交付实施敏捷开发。
- 可以在工具、架构和开发流程不发生明显变化的前提下实现扩展。
这套理论适用于任意语言和后端服务(数据库、消息队列、缓存等)开发的应用程序。
12-factors 包含:
- I. 基准代码 一份基准代码,多份部署
- II. 依赖 显式声明依赖关系
- III. 配置 按不同的运行环境,对配置信息进行独立的存放 / 使用
- IV. 后端服务 把后端服务当作附加资源
- V. 构建,发布,运行 严格分离构建和运行
- VI. 进程 以一个或多个无状态进程运行应用
- VII. 端口绑定 通过端口绑定提供服务
- VIII. 并发 通过进程模型进行扩展
- IX. 易处理 快速启动和优雅终止可最大化健壮性
- X. 开发环境与线上环境等价 尽可能的保持开发,预发布,线上环境相同
- XI. 日志 把日志当作事件流
- XII. 管理进程 后台管理任务当作一次性进程运行
Spring Cloud 提供一种途径,使得能够方便快捷并按需的搭建一个可用的分布式应用。
在使用Spring Cloud 时很多特性是基于Spring Boot的, 另外还有两个库:Spring Cloud Context 和 Spring Cloud Commons。
Spring Cloud Context
提供一些工具,以及Spring Cloud应用(ApplicationContext)的基础服务。如:引导上下文;加解密;作用域重建;环境()
Spring Cloud Commons
是一个不同Spring Cloud实现的抽象层以及通用类集合。 如:Spring Cloud Netflix;Spring Cloud Consul
如果出现“Illegal key size”错误,那表示需要更新JCE策略文件,以获得更高的权限
1 Spring Cloud Context: Application Context Services
Spring Boot 提供了一套固定的套路来使用spring构建应用,用一种约定俗成的方式来进行配置,并提供一套通用的管理、监控方式。 Spring Cloud 在此基础之上,增加一些可以按需配置的功能。
1.1 The Bootstrap Application Context 引导上下文
一个Spring Cloud应用,首先会创建一个引导上下文来作为主应用上下文的父级。 负责从外部加载配置源。两个上下文共享一个
Environment
来做为Spring应用的外部配置源。
默认的引导属性拥有更高的优先级,因此,不能被本地配置所覆盖。
引导上下文使用了一个和Spring Boot主应用不同的约定方式来加载配置。引导上下文使用
bootstrap.yml
(或者properties)来配置,通过这样很好的与主应用配置分离开来。
bootstrap.yml
spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
引导上下文也可以通过在系统属性中配置:
spring.cloud.bootstrap.enabled=false
来关闭。
1.2 Application Context Hierarchies 应用上下文层级
如果使用
SpringApplication
或者SpringApplicationBuilder
来构建上下文时,那么会添加一个 引导上下文(BootstrapContext
)会作为父级上下文。
子级上下文会从父级上下文继承配置源以及配置文件,因此主应用会包含额外的配置来源。
额外的配置源包括:
- "bootstrap":如果有任何非空
PropertySourceLocators
被发现,那会生成一个优先级更高的CompositePropertySource
- "applicationConfig:[classpath:bootstrap.yml]":如果配置了
bootstrap.yml
或properties
,那么他们的配置项将被用于引导上下文(BootstrapContext
),这样也会被子级上下文引用到。他们比application.yml
的优先级低。
由于加载顺序使得
bootstrap
配置源会先被扫描,但是,注意:由于处于一个非常低的优先级,所以并没有马上加载bootstrap.yml
中的数据,也正是这个原因,通常被用来当作默认配置实用。
可以设置父级上下文(
ApplicationContext
)来自己扩展上下文层级。 可以自己实现接口,也可以通过SpringApplicationBuilder
提供的几个方便的工具方法(parent()
,parent()
,parent()
)。 不论自定义了多少上下文层级,bootstrap
引导上下文都处于最高级。 在这个层级中的每一个上下文都能访问到引导上下文的配置源,使用时需要注意避免无意的配置覆盖情况。 层级中的每一个上下文原则上都应该有一个不同的spring.application.name
,因此当有配置服务器时,就会有一个不同的远程配置源。
普通的上下文,有一个区分配置的规则:子级上下文中的属性会覆盖父级上下文中的属性,覆盖条件包含属性名和配置源名。也就是说可以对整个配置源进行覆盖。
注意:
SpringApplicationBuilder
允许在整个上下文层级中共享Environment
,但此功能默认关闭。因此,当需要时,同级别的上下文不需要去包含同一个配置源,他们可以和父级上下文共享这些配置。
1.3 Changing the Location of Bootstrap Properties 改变引导上下文配置路径
可以通过配置
spring.cloud.bootstrap.name
来改变默认的引导上下文配置文件名。(默认值bootstrap
)。
也可以通过配置
spring.cloud.bootstrap.location
来改变默认的引导上下文配置的扫描路径。(默认值空
)
通过系统属性来改变以上两个配置项
配置文件中
spring.config.*
等配置项是被用于引导上下文的配置,并被加载到Environment
中。
如果配置了
spring.profiles.active
或者通过Environment
的api来指定一个特定配置项,那么这个特定配置文件也会被加载,就如同一个Spring Boot应用一样。
1.4 Overriding the Values of Remote Properties 覆盖远程配置项
被引导上下文加载的配置源通常都是一个远程的(例如:Config Server),默认情况下远程配置项不能被本地配置所覆盖。
如果应用想要覆盖远程配置项,则需要远程配置源通过配置
spring.cloud.config.allowOverride=true
来授权。一旦开启此功能,则可以在本地配置下面两个配置项来控制细节:
spring.cloud.config.overrideNone=true
覆盖本地所有配置源;spring.cloud.config.overrideSystemProperties=false
仅覆盖系统属性和环境变量,而不覆盖本地配置文件。
1.5 Customizing the Bootstrap Configuration 定制引导配置
可以在
/META-INF/spring.factories
中添加org.springframework.cloud.bootstrap.BootstrapConfiguration
配置,来指定任何想要添加到引导上下文中的配置类(@Configuration
),如果有多个可以用逗号分隔。任何想要在主应用中自动装配的spring bean 都可以通过这些配置类来定义。 配置类也可以是一个包含@Beans
的ApplicationContextInitializer
对象。 当配置类有一定依赖顺序时,可以通过@Order
来指定顺序,默认:last。
注意: 当使用自定义
BootstrapConfiguration
时,在主应用时要小心使用@ComponentScanned
防止不必要的加载。最好使用一个特定的包名来放置那些自定义配置类,来避免与@ComponentScan
,@SpringBootApplication
重叠。
当引导处理结束时会自动注入到主应用的
SpringApplication
实例中。 在启动之前,引导上下文会找到spring.factories
中的配置类以及所有被标记@Beans
的ApplicationContextInitializer
对象加入到主应用S上下文(SpringApplication
)
1.6 Customizing the Bootstrap Property Sources 定制引导上下文的配置源
默认情况下外部配置源是一个Config Server,当然也可以在
spring.factories
中配置PropertySourceLocator
对象来手动添加外部配置源(其他Config Server;数据库等)
例如:
@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"));
}
}
任何一个
ApplicationContext
都会创一个Environment
对象,它包含了外部配置源。它也类似一个普通Spring Boot配置源,因此你可以通过它来定制本地配置项。(例如:spring.application.name
)
如果jar中包含上面代码中的class,并且在
META-INF/spring.factories
包含:org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
那么,这个自定义配置源将会在classpath下所有的应用中被使用。
1.7 Environment Changes 改变环境
应用会监听
EnvironmentChangedEvent
事件,可以通过ApplicationListeners
来响应这个事件。
当配置项发生变动时,
EnvironmentChangedEvent
事件被抛出。 此时,应用会做出:
- 重建上下文中所有的
@ConfigurationProperties
对象- 对所有的日志属性
logging.level.*
进行设定
- 注意: Config Client默认不会主动去检测
Environment
的改变,不过可以通过一个定时任务(@Scheduled
)去轮询。但是,通常来说并不建议用这样的方式。最好,通过广播EnvironmentChangedEvent
事件这种方式来处理。(例如使用:Spring Cloud Bus)
由于
EnvironmentChangedEvent
事件是通过Spring标准API来完成的,这就导致当Environment
发生改变时,会有大量的对象刷新。 例如:动态配置一个DataSource
的连接池的maxPoolSize
,当maxPoolSize
发生改变时,导致整个ApplicationContext
进入一个阻塞的重建@ConfigurationProperties
阶段。 为了解决这个问题,需要使用@RefreshScope
1.8 Refresh Scope 刷新范围
可以在
@Bean
上标记一个@Refresh Scope
来指定这个Bean需要在配置改变时刷新。
Refresh scope 对象都是延迟代理初始化的,调用方法获取值时都是从初始化值得缓存中获取的。重新初始化其实也就是初始化缓存。
RefreshScope
的refreshAll()
方法可以刷新全部目标缓存。refresh(String)
方法可以指定名字的缓存。这个功能会通过/refresh
暴露到外部(如:HTTP,JMX)
注意:
@RefreshScope
工作于@Configuration
类,这样就会导致一个特殊的情况:互相依赖的Bean也会被刷新重建,并重新注入,这样@Configuration
也就被重新初始化
1.9 Encryption and Decryption 加解密
Spring Cloud 为
Environment
提供了一个本地解密属性的预处理过程。 此过程,会使用几个扩展配置:encrypt.*
这样就可以通过{cipher}*
来使用加密数据了。
如果需要使用这个特性,那就需要引入Spring Security RSA 到classpath。
同样这也需要对JCE进行扩展。
1.10 Endpoints 入口
对于一个基于Spring Boot Actuator应用,Spring Cloud原生应用扩展几个管理入口:
/env
(POST) : 用于 更新Environment
;重建@ConfigurationProperties
;日志级别/refresh
: 重新加载引导上下文;刷新@RefreshScope
对象。/restart
:重启ApplicationContext
,此功能默认不可用/pause
和/resume
: 调用Lifecycle
方法 (相当于ApplicationContext
的stop()
和start()
方法)
2 Spring Cloud Commons: Common Abstractions 共用抽象层
如服务发现,负载均衡,断路器等模式都是以一个共用抽象层提供给所有的Spring Cloud客户端独立使用。
2.1 Spring RestTemplate as a Load Balancer Client 负载均衡
RestTemplate
可以使用@LoadBalanced
在对应的@Bean
进行连带的配置,就可以做成负载均衡。
注意:
RestTemplate
不再自动创建了,需要时,需要自己创建特定的应用
@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.2 Multiple RestTemplate objects 多重RestTemplat
如果
RestTemplate
不想要负载均衡,那就创建一个普通RestTemplate
正常注入就行了。 如果需要负载均衡,在创建@Bean
时加上@LoadBalanced
限定就行。
如果需要混用的话,可以参见下面的例子:
@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
混淆。 如果上面的代码在进行RestOperations
操作时,出现java.lang.IllegalArgumentException
异常,可以设置spring.aop.proxyTargetClass=true
来解决。
2.3 Ignore Network Interfaces 忽略网络接口
有的时候需要忽略某个名字的网络接口,把他们排除在服务发现注册之外。(如:运行于Docker容器) 可以设置一组正则表达式来对接口名进行匹配,来进行忽略这些接口:
application.yml
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*