分布式配置中心Spring Cloud Config
文章目录
起源
为分布式系统中的基础设施或者微服务提供集中化(统一)的外部配置支持,并实现在线更新。
组成
从之前的示例图上,我们可以看到它也是分为服务端和客户端两部分的,其中服务端就是分布式配置中心,它也是一个独立的微服务应用。用来为客户端提供配置,当然它的配置信息可以是本地的也可以是一个远程的配置仓库(通常我们都会是从一个独立的GIT仓库中获取,你肯定知道GIT是可以做版本管理的)。
需要实现统一管理或在线更新配置的其他微服务,扮演的就是客户端的角色。它们从服务端(配置中心)中获取业务相关的配置内容,会在启动或收到总线通知(下一章节会讲到)的时候去加载配置。
配置与映射
服务端映射关系说明
访问配置信息的URL与配置文件的映射关系有如下几种:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
application:配置文件中除去最后环境信息的部分
profile:环境
label:分支信息,默认是master分支
示例说明:config-client-dev.properties配置文件中,config-client是application,dev是profile
实际案例
spring:
application:
name: config-server
cloud:
config:
server:
git:
#仓库配置
uri: https://github.com/hzqiuxm/SpringcloudConfig
#相对搜索位置,可以多个
search-paths: respo
#分支
label: master
server:
port: 8888
github工程下信息:
使用postman工具进行访问:
输入:localhost:8888/config-client/dev
返回信息:
{
"name": "config-client",
"profiles": [
"dev"
],
"label": null,
"version": "c6d735d09f5733200a02f82b7f89e536d8842fa5",
"state": null,
"propertySources": [
{
"name": "https://github.com/hzqiuxm/SpringcloudConfig/respo/config-client-dev.properties",
"source": {
"democonfigclient.message": "hello spring io",
"foo": "foo version 1"
}
},
{
"name": "https://github.com/hzqiuxm/SpringcloudConfig/respo/config-client-dev.yml",
"source": {
"name": "hzqiuxm"
}
}
]
}
输入:localhost:8888/config-client/dev
, 注意最后的dev代表是分支
返回:
{
"name": "config-client",
"profiles": [
"dev"
],
"label": "dev",
"version": "7df1a8d5c8f0b2e2c64f27284a6e26e4e3615296",
"state": null,
"propertySources": [
{
"name": "https://github.com/hzqiuxm/SpringcloudConfig/respo/config-client-dev.properties",
"source": {
"democonfigclient.message": "hello spring io",
"foo": "foo version 3"
}
},
{
"name": "https://github.com/hzqiuxm/SpringcloudConfig/respo/config-client-dev.yml",
"source": {
"name": "hzqiuxm"
}
}
]
}
客户端配置映射
- 注意客户端的配置文件名:bootstrap.properties(.yml)
- spring.application.name={application部分}
- spring.application.config.profile={profile部分}
- spring.application.config.label={label部分}
- spring.application.config.uri={配置中心的地址,带端口}
原理
完整基础架构
之前的示例其实还有一个不完整的地方,就是配置中心在获取了GIT仓库中的配置后,会在本地存储一份,这样不用每次都去远程仓库去取了,即便远程仓库暂时无法访问,也可以保证能返回配置信息给客户端。
完整的基础架构示例图如下:
客户端从配置管理中获取配置流程:
1 客户端根据bootstrap.properties文件配置信息,向配置中心请求配置
2 配置中心查找客户端需要配置信息
3 通过git clone 命令将远程git仓库获取的配置下载到本地的git仓库中
4 配置中心创建spring的ApplicationContext实例,从本地获取配置信息给客户端
5 客户端得到配置文件,加载到ApplicationContext实例中(该配置优先级高于jar包内部的配置)
Git仓库配置
- 占位符配置: 在配置文件中使用{applicaion}方式配置,服务端会根据客户端的spring.application.name来填充(类似的还有其他几种占位符)
最佳实践:代码库和配置库名称统一,比如代码库为user-service的微服务的配置库就是user-service-config,这样配置的时候就可以使用占位符{application}来实现通用的配置了
- 多仓库配置:除了使用application和profile模式陪陪配置仓库外,还可以使用通配符的模式,在配置文件中配置多个仓库来应对复杂的配置需求(建议不要太复杂,维护困难)
- 指定本地仓库位置:spring.cloud.config.server.git.basedir来配置一个我们准备好的目录
- 属性覆盖:实现服务端和客户端共同配置,客户端也可以覆盖。通过spring.cloud.config.server.overrides属性来设置键值对参数
- 安全保护:结合SpringSerurity实现用户名密码访问
加密解密
为了保证数据库账户密码的安全性,不能把明文暴露给开发人员,所以需要对密码进行加密
配置成:{cipher}xxxxxxxxxxxxxxxxxx 的值时,因为{cipher}前缀的作用,配置中心会自动为该值进行解密
准备阶段
- 去除密钥长度限制:因为需要用到AES加密,而JDK自带的算法密钥长度是有限制,我们要先下一个授权文件,去除长度限制
> JCE下载:https://www.oracle.com/search/results?Nty=1&Ntk=S3&Ntt=JCE 根据自己的JDK版本选择对应版本解压的授权文件:local_policy.jar
,US_export_policy.jar
复制到$JAVA_HOME/jre/lib/security目录下,覆盖掉原来内容即可 - 通过新端点验证:GET访问
/encrypt/status
端点,提示你 “NO_KEY”就说明成功了 - 配置密钥:在配置文件增加
encrypt.key=xxxx
xxx是你设置的密钥(更好的方式是使用环境变量ENCRYPT_KEY进行外部存储配置) - 使用:使用
/encrypt
和/decrypt
端点对请求的body内容进行加解密,它们是post请求
默认采用的是AES对称加密算法,当然也可以采用非对称加密来实现,这就需要我们自己生成密钥对,配置中加入密钥对的位置信息
高可用配置
传统方式
不需要为服务端做任何的额外的配置,所有的服务端都指向一个GIT仓库,通过nginx等其它软件或硬件来实现负载均衡
微服务模式
将服务端作为微服务注册到注册中心,这样客户端调用的时候就可以去注册中心查询,再通过声明式调用或者Riboon来实现客户端的负载均衡
增加配置,注册到注册中心:
eureka:
client:
service-url:
#注册中心地址
defaultZone: http://localhost:8761/eureka/
客户端相关说明
URL指定配置中心
- 只有我们配置了
spring.cloud.config.uri
的时候,客户端才会尝试去连接服务端来获取远程配置信息 - 配置必须是在bootstrap.properties文件中
失败快速响应与重试
如果你想客户端优先判断能否从服务端获取配置,并快速响应失败的内容,增加下面参数配置:
spring.cloud.config.failFast=ture;
如果只是因为网络波动导致的失败,那代价未免大了些,所以我们希望最好能够自动重试,避免是网络波动原因造成的偶发失败。
开启自动重试需要依赖spring-retry和spring-boot-starter-aop,当然上面失败快速响应的配置也是要的。无需其他额外的配置,这样就实现了失败的自动重试(默认会重试6次,你可以对最大重试次数和重试间隔进行配置)
动态刷新配置
有时候我们需要对配置内容做一些实时更新,这个时候需要引入spring-boot-starter-actuator监控模块,我们主要是用该模块的/refresh端点进行实现的,该端点将用于实现客户端应用配置信息的重新获取与刷新。
熟悉Git仓库的人可能就想到了,我们能够和Web Hook进行关联,这样Git配置变化时,就主动给配置主机发送/refresh请求,实现配置的自动更新(不过维护刷新清单是件烦人的事情,最好的解决方案是通过消息总线)。
简单示例
配置中心(服务端)端口:8888
客户端端口:8881
注册中心端口:8761
远程GIT仓库配置:
master分支下的dev配置文件内容
客户端访问主要代码,简单的获取一个参数值:
@EnableDiscoveryClient
@SpringBootApplication
@RestController
@RefreshScope
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@Value("${foo}")
String foo;
@RequestMapping(value = "/hi")
public String hi(){
return foo;
}
}
访问:localhost:8881/hi
返回响应: foo version 1
修改远程仓库内容的值:
发送POST请求: localhost:8881/refresh
返回响应:
[
"config.client.version",
"foo"
]
如果响应不成功,提示没有权限的话,在配置中增加:
management.security.enabled = flase
,其默认是true
重新访问:localhost:8881/hi
返回响应: foo version 1 add something