使用Spring Cloud Config统一管理配置,别再到处放配置文件了
1 前言
欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章!
可配置是一个成熟软件系统应该提供的特性,而配置管理对于大型系统就显得十分重要,特别是对于拥有多个应用的微服务系统。可喜的是,Spring为我们提供了很好的配置管理,如Springboot的配置就很强大。对于Spring Cloud,就有强大的Spring Cloud Config,在提供了一个在应用之外的配置管理,如文件或Git仓库,对分布式系统配置管理十分有益。
2 快速体验
Spring Cloud Config服务端就是一个Springboot应用,启动、部署都十分简单。
整体的架构如下图所示:

2.1 服务端就是一个Springboot
在Springboot中添加依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
只需要一个就行了,它已经包含了web和actuator。
添加Java主类:
package com.pkslow.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class,args);
}
}
跟普通的Springboot应用相比只是多了一个注解@EnableConfigServer而已。
2.2 配置仓库
通过我们会把配置通过版本控制管理起来,一般使用Git仓库,为简单展示使用本地仓库如下:
# 创建目录
mkdir git-repo
# 初始化一个git目录
git init
# 新建文件
touch application.properties
# 添加变更
git add .
# 提交变更
git commit -m "init"
配置一下项目的application.properties,注意是Config Server项目的,而不是在git-repo目录里的:
server.port=8888
spring.application.name=config-server
spring.cloud.config.server.git.uri=/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo
接着就可以启动Config Server了。
但git仓库里的配置文件没有什么内容,我们加入以下内容并提交(必须要提交,不然无法获取)。
pkslow.webSite=www.pkslow.com
pkslow.age=18
pkslow.email=admin@pkslow.com
2.3 配置路径匹配
那我们如何获取这些配置呢?可以通过以下URL读取:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
label指的是代码分支,如master,feature-1等。application是应用的名字,在以后客户端读取会用到。profile一般用于指定环境,如prod,dev,uat等。
所以,我们可以用以下URL来获取我们刚添加的配置信息:
http://localhost:8888/application/default
http://localhost:8888/application/default/master
http://localhost:8888/master/application.properties
http://localhost:8888/application-default.properties
访问如下:
$ curl http://localhost:8888/application/default/master
{"name":"application","profiles":["default"],"label":"master","version":"8796f39b35095f6e9b7176457eb03dd6d62b1783","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.age":"18","pkslow.email":"admin@pkslow.com"}}]}
最后一个地址/{label}/{application}-{profile}.properties的返回结果格式不同,直接返回配置文件内容:
$ curl http://localhost:8888/application-default.properties
pkslow.age: 18
pkslow.email: admin@pkslow.com
pkslow.webSite: www.pkslow.com
如果我们先建一个分支release-20200809,并修改age为实际年龄9,则如下:
$ curl http://localhost:8888/application/default/release-20200809
{"name":"application","profiles":["default"],"label":"release-20200809","version":"7e27e6972ed31ee1a51e9277a2f5c0a628cec67a","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.age":"9","pkslow.email":"admin@pkslow.com"}}]}
可以看到对应的pkslow.age已经变为9了,但访问/application/default/master则还是18,分支之间不会相互影响。
2.4 远程仓库
本地仓库只是为了简单展示,在实际项目中,一般使用远程仓库,在GitHub创建一个新的仓库如下:

特意创建了个private的仓库来检测后面的鉴权是否正确。
重新配置仓库的地址如下:
spring.cloud.config.server.git.uri=https://github.com/pkslow/pkslow-config
spring.cloud.config.server.git.username=admin@pkslow.com
spring.cloud.config.server.git.password=***
spring.cloud.config.server.git.default-label=master
spring.cloud.config.server.git.search-paths=demo
创建一个demo目录来放置配置,所以search-paths配置为demo。完成配置重启服务器,就可以正常读取远程仓库的配置了。
2.5 多个代码配置仓库
有些时候,我们的配置可能并不只在一个仓库里,而是在各自客户端的代码库中,比如我们有以下三个服务:
- (1)服务发现:discovery,代码库pkslow-discovery-service
- (2)API网关:gateway,代码库pkslow-gateway-service
- (3)订单服务:order,代码库pkslow-order-service
它们各自的配置文件都放在各自的代码库里,那就需要配置多个代码库。我们还多配置一个默认的配置库pkslow-default,如果匹配不到,就会选择默认代码库的配置。具体配置如下:
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: /Users/pkslow/multiple-repos/pkslow-default
repos:
pkslow-discovery-service:
pattern: pkslow-discovery-*
cloneOnStart: true
uri: /Users/pkslow/multiple-repos/pkslow-discovery-service
search-paths: config
pkslow-gateway-service:
pattern: pkslow-gateway-*/dev
cloneOnStart: true
uri: /Users/pkslow/multiple-repos/pkslow-gateway-service
search-paths: config
pkslow-order-service:
pattern: pkslow-order-*
cloneOnStart: true
uri: /Users/pkslow/IdeaProjects/pkslow-order-service
search-paths: config
可以各自定义配置文件所放的目录search-paths,不配置默认为根目录。
这里的pattern的配置规则是{application}/{profile},支持正则符号*。注意只匹配一个结果,如果都满足,只取第一个匹配的仓库。
启动后我们来看看配置结果:
# 默认profile和label,正确匹配
$ curl http://localhost:8888/pkslow-order-service/default/master
{"name":"pkslow-order-service","profiles":["default"],"label":"master","version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"order-service"}}]}
# 正确匹配,但不存在的Label,配置库没有对应代码分支,404
$ curl http://localhost:8888/pkslow-order-service/default/release
{"timestamp":"2020-08-13T06:58:38.722+0000","status":404,"error":"Not Found","message":"No such label: release","path":"/pkslow-order-service/default/release"}
# profile为dev,正确匹配
$ curl http://localhost:8888/pkslow-order-service/dev/master
{"name":"pkslow-order-service","profiles":["dev"],"label":"master","version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"order-service"}}]}
# 对于gateway只能匹配profile=dev,无法匹配,读取默认配置
$ curl http://localhost:8888/pkslow-gateway-service/default/master
{"name":"pkslow-gateway-service","profiles":["default"],"label":"master","version":"8358f2b4701fac21a0c7776bc46cec6d9442c549","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties","source":{"pkslow.birthDate":"2020-08-10"}}]}
# 对于gateway只能匹配profile=dev,正确匹配
$ curl http://localhost:8888/pkslow-gateway-service/dev/master
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"master","version":"1a4e26849b237dc2592ca0d391daaa1a879747d2","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"gateway-service"}}]}
# 不存在的服务名,无法匹配,读取默认配置
$ curl http://localhost:8888/unknown-service/dev/master
{"name":"unknown-service","profiles":["dev"],"label":"master","version":"8358f2b4701fac21a0c7776bc46cec6d9442c549","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties","source":{"pkslow.birthDate":"2020-08-10"}}]}
3 客户端使用配置
经过前面的例子我们已经了解到服务端如何从代码库里获取配置,但始终还是要使客户端能获取到对应的配置并产生效果。我们来演示一下。
3.1 项目准备
搭建一个最简单的Springboot Web项目,并加上Spring Cloud Config的支持,添加依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
添加配置文件:bootstrap.properties(虽然我们要从服务端读取配置,但有些配置还是必须在客户添加的,如服务端地址),内容如下:
server.port=8080
spring.application.name=pkslow-gateway-service
spring.cloud.config.uri=http://localhost:8888
在这里我们配置了客户端的端口、服务端的地址以及客户端应用的名字,这个名字是非常关键的,待会讲解。
添加Controller来暴露API以显示读到的配置内容:
@RestController
public class PkslowController {
@Value("${pkslow.age}")
private Integer age;
@Value("${pkslow.email}")
private String email;
@Value("${pkslow.webSite}")
private String webSite;
@GetMapping("/pkslow")
public Map<String, String> getConfig() {
Map<String, String> map = new HashMap<>();
map.put("age", age.toString());
map.put("email", email);
map.put("webSite", webSite);
return map;
}
}
接着启动客户端即可,访问结果如下:
$ curl http://localhost:8080/pkslow
{"webSite":"default.pkslow.com","age":"9","email":"admin@pkslow.com"}
这些配置内容并不在客户端,说明可以从服务端获取到配置信息了。
3.2 客户端如何匹配
客户端已经获取到配置信息了,那是否正确呢?客户端又是怎样匹配的呢?其实前面的内容已经提及,主要通过三个信息来匹配:
label指的是代码分支。application是应用的名字。profile一般用于指定环境。
上个例子客户端名字为pkslow-gateway-service;label没指定,默认为master;profile没有指定,默认为default。
我们在服务端匹配规则为pattern: pkslow-gateway-*/dev,所以可以匹配名字,但无法匹配profile,因此读取了默认仓库的配置:
$ cat pkslow-base/application.properties
pkslow.webSite=default.pkslow.com
pkslow.age=9
pkslow.email=admin@pkslow.com
修改一下客户端的配置,配置profile为dev如下:
server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.uri=http://localhost:8888
再次获取客户端的配置如下:
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-master.pkslow.com","age":"9","email":"admin@pkslow.com"}
可见已经读取到pkslow-gateway-service仓库的配置内容了:
$ cat pkslow-gateway-service/config/application.properties
pkslow.webSite=gateway-master.pkslow.com
pkslow.age=9
pkslow.email=admin@pkslow.com
在pkslow-gateway-service仓库新建代码分支release并添加配置,再修改客户端配置如下:
server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.label=release
spring.cloud.config.uri=http://localhost:8888
重启后再次访问,正确读取到了新分支的配置:
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"9","email":"admin@pkslow.com"}
3.3 客户端配置生效问题
当我们修改配置后,再次访问结果如下:
$ git commit -a -m "update"
[release 0e489fe] update
1 file changed, 2 insertions(+), 2 deletions(-)
$ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"release","version":"0e489fec5de73b1a6d11befa3f65e44836979e23","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"gateway-release.pkslow.com","pkslow.age":"10","pkslow.email":"admin@pkslow.com"}}]}
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"9","email":"admin@pkslow.com"}
结果发现,服务端已经生效了,但客户端并没有。这是因为在这种模式下客户端只会在启动的时候读取配置使其生效。如果想要客户端也生效,我们要使用Springboot actuator提供的/refresh端点才行。
先添加actuator依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置要保证/refresh可以访问,添加如下配置:
management.endpoints.web.exposure.include=refresh
指定刷新的范围,在Controller上添加注解@RefreshScope如下:
@RefreshScope
@RestController
public class PkslowController {
//xxx
}
重启应用。操作及效果如下:
# 修改配置并提交
$ git commit -a -m "update age to 18"
[release fc863bd] update age to 18
1 file changed, 1 insertion(+), 1 deletion(-)
# 服务端配置生效
$ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"release","version":"fc863bd8849fa1dc5eaf2ce0a97afb485f81c2f0","state":null,"propertySources":[{"name":"/Users/larry/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"gateway-release.pkslow.com","pkslow.age":"18","pkslow.email":"admin@pkslow.com"}}]}
# 客户端没有生效
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"10","email":"admin@pkslow.com"}
# 发送POST请求到客户端/refresh
$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version","pkslow.age"]
# 客户端已经生效
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"18","email":"admin@pkslow.com"}
3.4 自动更新配置
每次提交代码配置后,都需要手动发一次POST请求到客户端才能更新配置,显然是不够友好的。GitHub提供了webhook功能,可以在触发事件后,发送请求到指定URL,这样便可以实现自动更新了。
通过webhook功能实现自动更新是一对一的,如果客户端很多(通常是这种场景),就无法这样直接实现。有两个方案:
(1)自己实现一个端口来接受来自Git的请求,然后再分发到各个服务端。这个方法比较麻烦,不是很推荐。
(2)通过引入Spring Cloud Bus来刷新多个客户端。但需要引入MQ,如kafka或RabbitMQ。
3.5 在有服务发现时的不同
在微服务架构中,如果配置服务端与客户端都注册在服务发现(如eureka)上时,客户端就无须再配置服务端的地址了,会从服务发现中心获取识别。这与Springboot Admin在有eureka的情况下有异曲同工之妙。
代码没什么特别之外,就是把服务端和客户端同时注册到eureka即可。
4 总结
本文通过例子一步步展示如何使用Spring Cloud Config,主要是理解交互过程和匹配规则,其它都是代码细节,参考官方文档即可。
关于配置的一些文章:
Java怎么从这四个位置读取配置文件Properties(普通文件系统-classpath-jar-URL)
注解@ConfigurationProperties让配置整齐而简单
Springboot整合Jasypt,让配置信息安全最优雅方便的方式
使用Spring Cloud Config统一管理配置,别再到处放配置文件了
Spring Cloud Config整合Spring Cloud Kubernetes,在k8s上管理配置
欢迎关注微信公众号<南瓜慢说>,将持续为你更新...

多读书,多分享;多写作,多整理。
使用Spring Cloud Config统一管理配置,别再到处放配置文件了的更多相关文章
- .NET Core微服务之基于Steeltoe使用Spring Cloud Config统一管理配置
Tip: 此篇已加入.NET Core微服务基础系列文章索引 => Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...
- 9.Spring Cloud Config统一管理微服务配置
Spring Cloud Config统一管理微服务配置 9.1. 为什么要统一管理微服务配置 9.2. Spring Cloud Config简介 Spring Cloud Config为分布式系统 ...
- 【SpringCloud构建微服务系列】使用Spring Cloud Config统一管理服务配置
一.为什么要统一管理微服务配置 对于传统的单体应用而言,常使用配置文件来管理所有配置,比如SpringBoot的application.yml文件,但是在微服务架构中全部手动修改的话很麻烦而且不易维护 ...
- 【Spring Cloud】Spring Cloud Config 实现分布式配置中心
Spring Cloud Config 实现分布式配置中心 一.分布式配置中心 分布式系统中,往往拥有大量的服务应用,而每个应用程序都需要有对应的配置文件来协助完成服务环境初始化.运行.因此生产了大量 ...
- SpringCloud实战之初级入门(三)— spring cloud config搭建git配置中心
目录 1.环境介绍 2.配置中心 2.1 创建工程 2.2 修改配置文件 2.3 在github中加入配置文件 2.3 修改启动文件 3. 访问配置中心 1.环境介绍 上一篇文章中,我们介绍了如何利用 ...
- spring cloud config svn仓库配置
之前快速入门了一下spring cloud config 但是仓库用的别人博客上的git仓库,公司用的是svn项目管理中心,下面这个自己配置的时候出现的错误 You need to configure ...
- spring cloud config搭建说明例子(四)-补充配置文件
服务端 ConfigServer pom.xml <dependency> <groupId>org.springframework.cloud</groupId> ...
- Spring Cloud Config整合Spring Cloud Kubernetes,在k8s上管理配置
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Kubernetes有专门的ConfigMap和Secret来管理配置,但它也有一些局限性,所以还是希望通过Spring C ...
- springCloud学习-分布式配置中心(Spring Cloud Config)
1.简介 Spring Cloud Config :分布式配置中心,方便服务配置文件统一管理,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中.在spring cloud co ...
随机推荐
- pandas之表格样式
在juoyter notebook中直接通过df输出DataFrame时,显示的样式为表格样式,通过sytle可对表格的样式做一些定制,类似excel的条件格式. df = pd.DataFrame( ...
- myBatis源码解析-日志篇(1)
上半年在进行知识储备,下半年争取写一点好的博客来记录自己源码之路.在学习源码的路上也掌握了一些设计模式,可所谓一举两得.本次打算写Mybatis的源码解读. 准备工作 1. 下载mybatis源码 下 ...
- LFM oversea投资笔记、思摩尔、移卡、建业新生活、鑫苑物业
LFM oversea 1.公司简介 2.业务展望 3. 投资策略 LFM oversea介绍 LFM oversea是在开曼成立的投资基金, 专注于中国优质企业在香港和美国IPO过程中的 基石.锚定 ...
- KMP算法图解
字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...
- PHP array_diff_assoc() 函数
实例 比较两个数组的键名和键值,并返回差集: <?php$a1=array("a"=>"red","b"=>"g ...
- HTML 基础- 4个实例
HTML 基础- 4个实例 不要担心本章中您还没有学过的例子,高佣联盟 www.cgewang.com 您将在下面的章节中学到它们. HTML 标题 HTML 标题(Heading)是通过<h1 ...
- qemu 如何退出qemu
如何退出qemu ctrl + a 放开后,再按下x 这里字母是小写!(不要按着大写键)
- mysql8的深坑
主要记录下自己的出坑过程,有些其实并不是mysql8的问题,但是由于这个版本产生的阻塞时间最长,所以一并记录: 1-项目代码别人能运行,我本地运行不了 定位后发现是由于本地代码运行数据库连接失败,后来 ...
- 用var声明变量,在java中居然有这么多细节,你都了解么?
简介 Java SE 10引入了局部变量的类型推断.早先,所有的局部变量声明都要在左侧声明明确类型. 使用类型推断,一些显式类型可以替换为具有初始化值的局部变量保留类型var,这种作为局部变量类型 的 ...
- jenkins集成spring boot持续化构建代码
我个人使用的是阿里云的云服务器,项目采用的是spring boot为框架,现在要做的功能就是将本地开发的代码提交到github中,通过jenkins自动化集成部署到云服务器.接下来开始步骤. 1 首先 ...