Spring Cloud Config 配置中心实践过程中,你需要了解这些细节!
本文导读:
- Spring Cloud Config 基本概念
- Spring Cloud Config 客户端加载流程
- Spring Cloud Config 基于消息总线配置
- Spring Cloud Config 中的占位符
- Spring Cloud Config 仓库最佳实践
- Spring Cloud Config 健康检查问题剖析
本文主要介绍 Spring Cloud Config 基本概念、实践过的配置及遇到的问题进行剖析。关于如何启动运行配置中心可以参考官方 Demo。
本文使用的Spring Cloud 版本:Edgware.SR3
Spring Cloud Config 基本概念
Spring Cloud Config 用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持。
- 服务端:分布式配置中心,独立的微服务应用,用来连接配置仓库(GIT)并为客户端提供获取配置信息、加密/解密等访问接口。、
- 客户端:微服务架构中各个微服务应用和基础设施,通过指定配置中心管理应用资源与业务相关的配置内容,启动时从配置中心获取和加载配置信息
SCC作用:
实现了对服务端和客户端中环境变量和属性配置的抽象映射。
SCC优势:
默认采用 GIT 存储配置信息,天然支持对配置信息的版本管理。
Spring Cloud Config架构图:
基于消息总线的架构图:
如上图所示,架构图中的几个主要元素作用:
远程 GIT 仓库:
用来存储配置文件的地方。
Config Server:
分布式配置中心,微服务中指定了连接仓库的位置以及账号密码等信息。
本地 GIT 仓库:
在 Config Server 文件系统中,客户单每次请求获取配置信息时,Config Server 从 GIT 仓库获取最新配置到本地,然后在本地 GIT 仓库读取并返回。当远程仓库无法获取时,直接将本地仓库内容返回。
ServerA/B:
具体的微服务应用,他们指定了 Config Server 地址,从而实现外部化获取应用自己想要的配置信息。应用启动时会向 Config Server 发起请求获取配置信息进行加载。
消息中心:
上述第二个架构图是基于消息总线的方式,依赖的外部的 MQ 组件,目前支持 Kafka、Rabbitmq。通过 Config Server 配置中心提供的 /bus/refresh endpoint 作为生产者发送消息,客户端接受到消息通过http接口形式从 Config Server 拉取配置。
服务注册中心:
可以将 Config Server 注册到服务注册中心上比如 Eureka,然后客户端通过服务注册中心发现Config Server 服务列表,选择其中一台 Config Server 来完成健康检查以及获取远端配置信息。
Spring Cloud Config 客户端加载流程
客户端应用从配置管理中获取配置执行流程:
1)应用启动时,根据 bootstrap.yml 中配置的应用名 {application}、环境名 {profile}、分支名 {label},向 Config Server 请求获取配置信息。
2)Config Server 根据自己维护的 GIT 仓库信息与客户端传过来的配置定位去查找配置信息。
3)通过 git clone 命令将找到的配置下载到 Config Server 的文件系统(本地GIT仓库)
4)Config Server 创建 Spring 的 ApplicationContext 实例,并从 GIT 本地仓库中加载配置文件,最后读取这些配置内容返回给客户端应用。
5)客户端应用在获取外部配置内容后加载到客户端的 ApplicationContext 实例,该配置内容优先级高于客户端 Jar 包内部的配置内容,所以在 Jar 包中重复的内容将不再被加载。
Spring Cloud Config 基于消息总线配置
Config Server 作为配置中心 pom.xml 引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Config Server 配置文件(yml格式):
server:
port: ${CONFIG_SERVER_PORT:8021}
spring:
application:
name: letv-mas-config
# 配置了该项,访问/bus/refresh?destination=应用名:spring.application.index,如果未指定spring.application.index默认使用「应用名:server.port」
index: ${CONFIG_SERVER_IP:127.0.0.1}
cloud:
config:
server:
git:
# 基于 http 协议的单仓库,每一个应用创建一个目录,每个目录下创建配置文件
uri: ${GIT_URI:http://xxxx/config.git}
search-paths: '{application}'
# 配置的 Git 仓库基于 http 协议的,必须配置用户名和密码
username: ${GIT_USERNAME:config_server}
password: ${GIT_PASSWORD:config@123}
# 本地仓库目录设定
basedir: /letv/app/mas/config/repos
# 本地仓库如果有脏数据,则会强制拉取(默认是false)
force-pull: true
# 配置中心启动后从 GIT 仓库下载,如果uri配置中使用了 {application} 作为仓库名,这里要使用默认值false,否则启动报错.
clone-on-start: false
management:
security:
enabled: false
# 用户认证,客户端应用接入时加入安全认证配置
security:
user:
name: config
password: config2018
basic:
enabled: true
# 基于消息总线的 MQ 配置
spring:
cloud:
stream:
kafka:
binder:
zk-nodes: ${ZK_NODES:localhost:2181}
brokers: ${KAFKA_BROKERS:localhost:9092}
requiredAcks: -1
configuration:
security:
protocol: SASL_PLAINTEXT
sasl:
mechanism: PLAIN
jaas:
loginModule: org.apache.kafka.common.security.plain.PlainLoginModule
options:
username: test
password: test-secret
# 开启跟踪事件消息(默认是false)
bus:
trace:
enabled: true
# 自定义 topic 主题
destination: test.springcloud.config
Config Client 作为客户端 pom.xml 引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Config Client 配置文件(yml格式):
spring:
application:
name: letv-mas-client
index: ${CLIENT_SERVER_IP:127.0.0.1}:${server.port}
profiles:
active: ${CLIENT_PROFILE:default}
#include: busdev,streamdev
cloud:
config:
uri: ${CONFIG_SERVER_DOMAIN:http://config.xxx.cn/}
failFast: true #the client will halt with an Exception
enabled: true
# boostrap.yml 配置优先于启动参数变量 spring.profiles.active
profile: ${spring.profiles.active:${CLIENT_PROFILE:default}}
label: master
# 访问配置中心,用户安全认证
username: config
password: config2018
# 激活定时任务,当 GIT 版本发生变更后加载最新配置上下文
watcher:
enabled: true
security:
user:
name: config
password: config2018
# 基于消息总线的 MQ 配置( Kakfa 队列),如果zipkin中也使用 Kafka 队列,那么需要通过binder 形式配置做隔离,否则会互相影响,无法下发配置消息。
spring:
cloud:
stream:
# 自定义开关
enabled: true
# 指定中间件
default-binder: config-kafka
binders:
config-kafka:
type: kafka
environment:
spring:
cloud:
stream:
kafka:
binder:
zkNodes: ${ZK_NODES:localhost:2181}
brokers: ${KAFKA_BROKERS:localhost:9092}
# 生产者确认,0、1、-1,默认为1。0为不确认,1为leader单确认,-1为同步副本确认。-1的情况下消息可靠性更高。
required-acks: -1
# 是否自动创建topic,默认为true。设为false的情况下,依赖手动配置broker相关topic>配置,如果topic不存在binder则无法启动。
auto-create-topics: true
configuration:
security:
protocol: SASL_PLAINTEXT
sasl:
mechanism: PLAIN
jaas:
loginModule: org.apache.kafka.common.security.plain.PlainLoginModule
options:
username: test
password: test-secret
bus:
# 是否启用bus
enabled: true
# Bus 使用的队列或 Topic,Kafka 中的 topic,Rabbitmq 中的 queue
destination: test.springcloud.config
trace:
# 是否启用 Bus 事件跟踪,可以通过 /trace 页面查看
enabled: true
refresh:
# 是否发送 refresh 事件,开启时支持基于 config 文件变更的动态配置
enabled: true
env:
# 是否开启 env 事件,开启时支持直接动态配置相应环境变量,如 /bus/env?arg1=value1&arg2=value2
enabled: true
Spring Cloud Config 中的占位符
占位符的使用:
这里的 {application} 代表了应用名,当客户端向 Config Server 发起获取配置请求时,Config Server 会根据客户端的 spring.application.name 信息来填充 {application} 占位符以定位配置资源的存储位置。
注意:{label} 参数很特别,如果 GIT 分支和标签包含 “/”,那么 {label} 参数在 HTTP 的 URL 中应用使用 “(_)” 替代,以避免改变了 URI 含义,指向到其他 URI 资源。
为什么要有占位符?
当使用 GIT 作为配置中心来存储各个微服务应用的配置文件时,URI 中的占位符的使用可以帮助我们规划和实现通用的仓库配置。
Spring Cloud Config 仓库最佳实践
本地仓库:
Spring Cloud 的 D、E 版本中默认存储到 /var/folders/ml/9rww8x69519fwqlwlt5jrx700000gq/T/config-repo-2486127823875015066目录下。
在 B 版本中,未实际测试过,存储到临时目录 /tmp/config-repo-随机数目录下。
为了避免一些不可预知的问题,我们设置一个固定的本地GIT仓库目录。
通过 spring.cloud.config.server.git.basedir=${user.home}/local-config-repo
如果${user.home}目录下发现local-config-repo不存在,在Config Server启动后会自动创建,并从GIT远程仓库下载配置存储到这个位置。
远程仓库实践:
单仓库目录:
每一个项目对应一个仓库
spring.cloud.config.server.git.uri=https://gitee.com/ldwds/{application}
多仓库目录:
同一个仓库下,每个项目一个目录
spring.cloud.config.server.git.uri=https://gitee.com/ldwds/config-repo-demo.git
spring.cloud.config.server.git.search-paths='{application}'
1)单仓库目录注意事项:
spring.cloud.config.server.git.uri=[https://gitee.com/ldwds/config-repo-demo/](https://gitee.com/ldwds/config-repo-demo/)
spring.cloud.config.serversearch-paths:’{application}'
客户端应用启动前,在 config-repo-demo 仓库下创建子目录,子目录名称就是配置中指定的spring.application.name 应用名。
否则,工程中引用的属性找不到,会报如下错误:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'from' in value "${from}"
2)多仓库目录注意事项:
这种方式不能设置参数
spring.cloud.config.server.git.force-pull=true
和 spring.cloud.config.server.git.clone-on-start=true
否则启动会报错,也很好理解因为使用了 {applicatoin} 作为占位符,没有指明具体的仓库名,所以无法强制拉取远程仓库配置。
如果你设置了本地仓库目录比如 spring.cloud.config.server.git.basedir=/data/config-repos/local-config-repo
Config Server 启动后会自动创建 /data/config-repos 目录,并创建 config-repo-随机数命名的仓库名录,这个仓库下的内容来自于健康检查的默认仓库app。
客户端应用启动后,会根据 {application} 应用名去查找该仓库,Config Server 从匹配 Git 仓库并 clone 到 config-repo-随机数的目录下。
如果 Config Server 重启了,客户端应用通过 /bus/refresh 刷新配置,因为并没有缓存之前的仓库名,所以会自动创建一个 config-repo-随机数 的仓库目录并从 Git clone 数据。
如果 Config Server 已有本地仓库,客户端重启或 /bus/refresh 刷新配置则 Config Server 不会重建新的仓库。
配置中心本地仓库执行原理:
本地仓库是否存在根据 basedir 目录下是否包含.git 隐藏文件。如果本地仓库不存在,则从远端仓库 clone 数据到本地;如果本地仓库存在,则从远程仓库 fetch 最新数据到本地;然后 checkout 到指定 label,从远端仓库 merge 数据,并获取当前 label 分支最新的HEAD 版本,以及默认的应用名 app 作为环境信息返回。
Spring Cloud Config健康检查问题剖析
健康检查 pom.xml 中引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
添加上述依赖后,默认开启健康检查。如果不需要健康检查,可以通过 spring.cloud.config.server.health.enabled=false
参数设定关闭。
如果配置为: spring.cloud.config.server.git.uri=[https://gitee.com/ldwds/config-repo-demo.git](https://gitee.com/ldwds/config-repo-demo.git)
默认开启了健康检查,我开始认为默认检查应用名称为 app,profiles 为 default,label 为 null进行监控(源码中看到的)。
但是 GIT 配置仓库下并没有 app 应用,此时访问 /health,监控状态仍然是 UP?
{
"status": "UP"
}
上述理解是错误的,原因分析如下:
这个主要跟配置中心指定的 GIT 仓库地址有关系,如果仓库地址指定的是 https://gitee.com/ldwds/{application} ,检查监视器会将 {application} 替换为默认应用名 app 作为仓库地址,此时会在 {user.home} 目录下创建 config-repo-随机数作为 {application} 应用的本地仓库(如:/Users/liudewei1228/config-repo-7949870192520306956)。
即使设置了
spring.config.server.git.basedir=${user.home}/local-config-repo/
也不会被使用到。
为什么?因为 {application} 作为仓库,是个动态的,可能会有多个 {application} 项目仓库,所以不会使用 basedir 特定目录作为本地仓库。
如下参数设置健康检查的配置:
spring.cloud.config.server.health.repositories.config-repo-demo.name=应用名
spring.cloud.config.server.health.repositories.config-repo-demo.label=分支
spring.cloud.config.server.health.repositories.config-repo-demo.profiles=环境变量
找到环境信息即显示状态 UP,此过程出现任何异常(如找不到仓库 NoSuchRespositoryException)就会显示 DOWN 状态。
在uri中包含 {application} 作为仓库情况下,客户端应用在启用前需提前创建好spring.application.name=config-client应用名作为仓库,否则会导致无法启用。(因为 {application} 被认为是一个项目仓库,并不是一个目录)。
源码详见:ConfigServerHealthIndicator.java 的 doHealthCheck 方法。
配置正确仓库的 name、label、profiles,访问 /health 接口显示 sources,这个 sources 中的地址无法访问的,实际只是一个标识的作用。
访问/health结果:
{
"status": "UP",
"repositories": [
{
"sources": [
"https://gitee.com/ldwds/config-repo-demo/config-client/config-client.properties";
],
"name": "config-client",
"profiles": [
"default"
],
"label": "master"
}
]
}
否则,找不到指定仓库的信息,只会显示如下信息:
{
"status": "UP",
"repositories": [
{
"name": "config-client",
"profiles": [
"default"
],
"label": "master"
}
]
}
最新 Spring Cloud Config 改进了很多问题,大家可以结合官网进一步学习了解。
本文对 Spring Cloud Config (Spring Cloud E 版本)的基本概念、基于消息总线的配置使用、仓库目录实践、健康检查的实践以及实践中遇到的问题进行了剖析,希望有使用到这个配置中心的朋友们有所帮助。
目前微服务架构中选型时,推荐使用国内开源的配置中心:Apollo配置中心(携程开源)、Nacos注册&配置中心(阿里巴巴开源)。
欢迎关注我的公众号,扫码关注,解锁更多精彩文章,与你一同成长~
Spring Cloud Config 配置中心实践过程中,你需要了解这些细节!的更多相关文章
- 跟我学SpringCloud | 第七篇:Spring Cloud Config 配置中心高可用和refresh
SpringCloud系列教程 | 第七篇:Spring Cloud Config 配置中心高可用和refresh Springboot: 2.1.6.RELEASE SpringCloud: Gre ...
- 微服务SpringCloud之Spring Cloud Config配置中心Git
微服务以单个接口为颗粒度,一个接口可能就是一个项目,如果每个项目都包含一个配置文件,一个系统可能有几十或上百个小项目组成,那配置文件也会有好多,对后续修改维护也是比较麻烦,就和前面的服务注册一样,服务 ...
- 微服务SpringCloud之Spring Cloud Config配置中心服务化
在前面两篇Spring Cloud Config配置中心的博客中都是需要指定配置服务的地址url:spring.cloud.config.uri,客户端都是直接调用配置中心的server端来获取配置文 ...
- spring cloud --- config 配置中心 [本地、git获取配置文件]
spring boot 1.5.9.RELEASE spring cloud Dalston.SR1 1.前言 spring cloud config 配置中心是什么? 为了统一管理配 ...
- SpringCloud学习笔记(7):使用Spring Cloud Config配置中心
简介 Spring Cloud Config为分布式系统中的外部化配置提供了服务器端和客户端支持,服务器端统一管理所有配置文件,客户端在启动时从服务端获取配置信息.服务器端有多种配置方式,如将配置文件 ...
- Spring Cloud Config 配置中心
请将远程配置文件的格式写对: 比如使用 *.yml 或者 *.properties yml: testconfig: testvalue properties: testconfig=testvalu ...
- 微服务SpringCloud之Spring Cloud Config配置中心SVN
在回来的路上看到一个个的都抱着花,吃了一路的狗粮,原本想着去旁边的工业园里跑跑步呢,想想还是算了,人家过七夕,俺们过巴西.上一博客学习了Spring Cloud Config使用git作为配置中心,本 ...
- Spring Cloud Config 配置中心高可用
详细参见 <Spring Cloud 与 Docker微服务架构实战> p163-9.10 Spring Cloud Config 与 Eureka 配合使用 p163-9.12 Conf ...
- Spring Cloud Config 配置中心 自动加解密功能 jasypt方式
使用此种方式会存在一种问题:如果我配置了自动配置刷新,则刷新过后,加密过后的密文无法被解密.具体原因分析,看 SpringCloud 详解配置刷新的原理 使用 jasypt-spring-boot- ...
随机推荐
- Socket I/O模型之select模型
socket网络编程中有多种常见的I/O模型: 1.blocking阻塞 2.nonblocking非阻塞 3.I/O multiplexing复用 4.signal driven 5.asynchr ...
- effective java 3th item1:考虑静态工厂方法代替构造器
传统的方式获取一个类的实例,是通过提供一个 public 构造器.这里有技巧,每一个程序员应该记住.一个类可以对外提供一个 public 的 静态工厂方法 ,该方法只是一个朴素的静态方法,不需要有太多 ...
- 牛客Wannafly挑战赛13-BJxc军训-费马小定理、分式取模、快速幂
参考:https://blog.csdn.net/qq_40513946/article/details/79839320 传送门:https://www.nowcoder.com/acm/conte ...
- UVA - 10462-Is There A Second Way Left? Kruskal求次小生成树
UVA - 10462 题意: 求次小生成树的模板题,这道题因为有重边的存在,所以用kruskal求比较好. #include <iostream> #include <cstdio ...
- poj 2649 Factovisors 对n!进行因数分解
Factovisors Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 4431 Accepted: 1086 Descr ...
- POJ 2391 Ombrophobic Bovines(Floyd+二分+最大流)
题目链接 题意:农场有F(1 <= F <= 200)片草地用于放牛,这些草地有P(1 <= P <= 1500)连接,农场的草地上有一些避雨点,奶牛们可以在避雨点避雨,但是避 ...
- avalon.js与 ajax使用的一个错误实例
http://www.cnblogs.com/smallstudent/diary/2016/04/06/5360605.html
- Python二元操作符
def quiz_message(grade): outcome = 'failed' if grade<50 else 'passid' print ('grade', grade, 'out ...
- java.util.Timer简介
Timer是用于管理在后台执行的延迟任务或周期性任务,其中的任务使用java.util.TimerTask表示.任务的执行方式有两种: 按固定速率执行:即scheduleAtFixedRate的两个重 ...
- length属性、length()方法和size()的方法的区别
JAVA 1. length属性是针对Java中的数组来说的,要求数组的长度可以用其length属性: 2.length()方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法 ...