第一部分 Cloud Native 应用
Cloud Native 是一种持续交付和价值驱动开发领域的最佳实践。
Spring Cloud基于Spring Boot,主要基于两个库:Spring Cloud Context和Spring Cloud Commons
Spring Cloud Context为ApplicationContext
提供公共组件和特殊的服务,例如:bootstrap context启动上下文、encryption加密、refresh scope作用域刷新、environment endpoints环境定制等
Spring Cloud Commons是一组对相关组件的高度抽象类集合,例如:Spring Cloud Netiflix vs Spring Cloud Consul
如果你使用Sun‘s JDK时候’碰到了“Illegal key size
”异常,你需要安装Java Cryptography Extension(JCE)Unlimited Strength Jurisdiction Policy Files.下载地址:
- Java 6 JCE
- Java 7 JCE
- Java 8 JCE
下载后解压到JDK/jre/lib/security
目录下(或者JRE/JDK x674/x86)
2.Spring Cloud Context:Application Context Services应用上下文服务
Spring Boot有通用的默认构建配置,比如:通用的配置文件、服务管理、监控任务管理等,Spring Cloud在这些基础之上又提供了一些有用的特性。
2.1 The Bootstrap Application Context 引用应用上下文
Spring Cloud通过创建一个bootstrap
上下文来运行,这个上下文是应用程序的父上下文环境,它负责加载外部配置资源文件中的配置属性,且可以对配置属性进行解密。这个上下文是一个共享Environment
,它是任何Spring应用程序的外部属性的来源。它有较高的优先级,因此默认情况下不能被本地配置重写覆盖。
它使用约定的配置文件,它的优先级高于主配置文件,主配置文件时application.yml
(application.properties
),而它的配置文件是bootstrap.yml
(bootstrap.properties
),下面是一个示例:
bootstrap.yml.
spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果你的程序需要一些特殊的配置需要从配置服务器中读取,建议设置spring.application.name
这个属性(在bootstrap.yml
或application.yml
)
可以通过设置spring.cloud.bootstrap.endabled=false
来禁用引导的相关处理
2.2 Application Context Hierarchies 应用上下文层次结构
如果你是从SpringApplication
或者SpringApplicationBuilder
来构建应用上下文,则会将Bootstrap Context设置为顶层上下文。这是Spring的功能,即它会从父上下文环境继承属性源和配置文件,因此与不使用Spring Cloud Config构建相同上下文相比,main context
将包含其他属性源,下面是这些属性源:
bootstrap
:当在Bootstrap context中出现PropertySourceLocators
,则可选CompositePropertySource
显示为高优先级,并且具有非空属性。比如从Spring Cloud Config Server服务器的属性。参考"below"学习自定义属性源内容applicationConfig
:[classpath:bootstrap.yml],假如你有bootstrap.yml/bootstrap.properties
,这个文件中的属性是用来配置引导上下文的,这个配置的优先级比application.yml/application.properties
以及创建Spring Boot的其他属性源的优先级低,所以在它的属性会被覆盖。
因为启动加载有先后顺序,但是需注意的是,因为bootstrap.yml
,所以它的优先级比较低,使用的是默认的配置
你可以通过简单的设置来扩展ApplicationContext
,比如使用接口,或者使用SpringApplicationBuilder
中的一些方便的方法(parent()
、child()
、sibling()
)。层次结构中的每个上下文都有自己的bootstrap
属性源(可能为空),这样可以避免子级的值覆盖父级的值。层次结构中的每个上下文原则上也可以有自己的spring.application.name
,因此如果有Config server上,则就有不同的远程属性源。普通的Spring 应用程序上下文行为规则适用于属性解析:子环境中的属性通过名称和属性源名称覆盖父环境中的属性。
请注意:SpringApplicationBuilder
允许您在整个层次结构中共享Environment
,但是这个不是默认值。因此同层级的上下文环境不需要具有相同的配置文件或属性资源,他们将共享父上下文的
2.3 Changing the Location of Bootstrap Properties 更改引导位置
可以通过spring.cloud.bootstrap.name
(默认是bootstrap
)或spring.cloud.bootstrap.location
(默认是空)来指定bootstrap.yml/bootstrap.properties
文件的位置,例如,在系统属性中的spring.config.*
,它们用来设置引导ApplicatoinContext
,如果你通过spring.profiles.active
或者Environment
API激活了"环境"配置,这些在指定的profile中的属性就会被加载,比如你可以自定义bootstrap-development.properties
来激活developement
环境
2.4 Overriding the Values of Remote Properties覆盖远程配置的值
默认情况下从远程配置服务器加载的属性值是不能被覆盖的,但是如果你想要使用本地的系统属性覆盖它们,需要使用spring.cloud.config.allowOverride=true
来激活(在本地设置不起作用),一旦设置了该值,就可以更细化的控制远程和本地的属性值,spring.cloud.config.overrideNone=true
设置覆盖本地属性源,spring.cloud.config.overrideSystemProperties=false
则只覆盖系统属性和环境变量被远程设置覆盖,但是本地配置的属性不会被覆盖
2.5 Customizing the Bootstrap Configuration
通过添加/META-INF/spring.factories
文件,在org.springframework.cloud.bootstrap.BootstrapConfiguration
下设置加载在项目中自定义并添加了@Configuration
类来指定加载到上下文中的配置。任何你想在系统启动时就自动注入到上下文中的bean
都可以给它添加@Bean
注解,类型是ApplicationContextInitializer
,可以通过@Order
注解来定制它们的加载顺序,默认的顺序是’last’
要注意不要将不需要的类添加到上下文中,如果必要建议单独定义包名
引导过程会将自定义的实例注入到SpringApplication
,其中spring.factories
会被bootstrap context创建,所有@Bean
注解的会被ApplicationContextInitializer
注入,它会早于spring.factories
创建。
2.6 Customizing the Bootstrap Property Sources
默认是加载配置服务器上配置资源,你也可以通过PropertySourceLocator
(在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"));
}
}
Environment
是ApplicationContext
自动创建的,它为我们提供外部配置来源。
如果你的jar文件中包含有/META-INF/spring.factories
,里面包含有
org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
那么customProperty
的配置会在jar所在的classpath下共享
2.7 Environment Changes改变环境
程序通过ApplicationListeners
下的多个方法监听EnvironmentChangeEvent
事件,当监听到EnvironmentChangeEvent
改变,就会触发以下动作:
- 重新绑定有
@ConfigurationProperties
注解的beans - 设置日志级别
注意:配置客户端默认不会轮询Environment
的改变,而且我们不推荐使用轮询的方法检测改变(尽管你可以使用@Scheduled
注解来实现),建议使用广播的方式来监听EnvironmentChangeEvent
,这样所有的实例都可以获得最新的变更,例如使用Spring Cloud Bus组件
环境变更会刷新大量的实例,可以通过测试来确保是否成功,如果你想改变其中一部分的属性,比如数据库的连接相关属性,就可以使用@RefreshScope
注解
2.8 Refresh Scope刷新作用域
如果@Bean
注解的类上有@RefreshScope
注解,它就会在配置更改的时候被特殊对待。就是说它只会对影响的属性敏感。比如我们的数据库连接更新,只会重新加载对应的配置,不会影响其他的属性。
RefreshScop有一个公共方法refreshAll()
来触发所有beans清空缓存,刷新配置。也有一个refresh(string)
方法指定刷新bean的属性,它是通过HTTP或者JMX来触发/refresh
实现的
2.9 Encryption and Decryption 加密解密
如果你在配置服务器上设置了encrypt.*
,你就可以用{cipher}*
来加密键的值,而且它们会在被客户端获取时候自动解密。
加密解密使用了Spring Secruity RSA,所以你需要把它们的依赖添加到classpath中,maven中需要添加spring-security依赖。而且需要完整的JCE支持,
如果你得到了Illegal key size
,说明你的JCE不是Java Cryptography Extension(JCE) Unlimited Strength Jurisdiction Policy Files
版,下面是下载地址
- Java 6 JCE
- Java 7 JCE
- Java 8 JCE
解压后放置到JDK/jre/lib/security
目录下
2.10 Endpoints端点
Spring Boot Actuator应用集成了很多的管理端点:
/env
来重新绑定属性和日志级别/refresh
重新引导上下文和刷新beans的作用域/restart
重新启动ApplicationContext
/pause
和/resume
调用生命周期中的(stop()
和start()
)方法来暂停和重启Application
3.Spring Cloud Commons:Common Abstractions 通用抽象
Spring Cloud Commons对像服务发现、负载均衡、断路器等做了通用抽象,这样方便Spring Cloud Clients来自定义使用的实现,比如服务发现可以使用Eureka或者Consul
3.1 @EnableDiscoveryClient服务发现客户端
你可以在/META-INF/spring.factories
中自定义你的服务发现组件和配置信息,使用键org.springframework.cloud.client.discovery.EnableDiscoveryClient
来配置,可以使用的服务发现实现有:Spring Cloud Netflix Eureka、Spring Cloud Consul Discovery和Spring Cloud Zookeeper Discovery
默认情况下DiscoveryClient
会被自动注册到远程的服务发现服务器端,你可以在@EnableDiscoveryClient
中设置autoRegister=false
来禁用它
3.2 ServiceRegistry 服务注册
ServiceRegistry
接口提供register(Registration)
和deregister(Registration)
来注册和卸载你实现了Registration
接口的服务
@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
private ServiceRegistry registry;
public MyConfiguration(ServiceRegistry registry) {
this.registry = registry;
}
// called via some external process, such as an event or a custom actuator endpoint
public void register() {
Registration registration = constructRegistration();
this.registry.register(registration);
}
}
每一个ServiceRegistry
的实现都会有它自己的注册实现逻辑
3.2.1 ServiceRegistry Auto-Registration自动服务注册
默认情况下ServiceRegistry
自动注册正在运行的服务,有两种方法禁止这种动作
@EnableDiscoveryClient(autoRegister=false)
spring.cloud.service-registry.auto-registration.enabled=false
3.2.2 Service Registry Actuator Endpoint 注册执行端点
/service-registry
,/service-registry/instance-status
GET请求会返回Registration
的状态。POST请求会返回字符串对象类型
3.3 Spring RestTemplate as Load Balancer Client 负载均衡请求模板
RestTemplate
由ribbon自动配置,使用@LoadBalanced
注解来创建一个负载均衡的请求模板对象RestTemplate
这个版本的Spring Cloud中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;
}
}
3.3.1 Retrying Failed Requests失败重试
负载均衡的RestTemplate
可以配置失败请求,默认情况下是未配置的,你可以添加Spring Retry到你的classpath,使它有失败重试的能力。
如果你想禁止Spring Retry的重试机制就设置spring.cloud.loadbalancer.retry.enabled=false
,这个属性可以用在client.ribbon.MaxAutoRetries
、client.ribbon.MaxAutoRetriesNextServer
、client.ribbon.OkToRetryOnAllOperations
,参考Ribbon文档
3.4 Multiple RestTemplate objects多重RestTemplate对象
如果你不想使用负载均衡,可以仅仅创建一个RestTemplate并注入就可以了,要使用负载均衡就在Bean上添加@LoadBalanced
注解
注意@Primary,这个注解是指定接口的实现的,用来消除不合适的自动注入
@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);
}
}
如果出现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
3.5 Ignore Network Interfaces忽略网络接口
如果服务器有多个网卡,有时候需要忽略掉其中的几个,下面的配置示例会忽略"docker0"的网卡,而启用"veth"
application.yml
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
也可以指定网络地址
application.yml
spring:
cloud:
inetutils:
preferredNetworks:
- 192.168
- 10.0
也可以设置为本地的地址,参考Inet4Address.html.isSiteLocalAddress()
application.yml
spring:
cloud:
inetutils:
useOnlySiteLocalInterfaces: true