一.介绍
Spring Cloud Config 是SpringCloud家族中较早的配置中心,SpringCloudConfig是分布式系统中,为服务端和客户端解决配置管理的方案。它提供了集中化的配置管理,使得在不同环境(开发、测试、生成)中管理应用程序配置变得更加简单和一致。它支持配置的动态刷新,允许在不重启应用的情况下更新配置,提高了系统的灵活性和响应速度。
Spring Cloud Config 是一个分布式配置管理系统,它主要包括以下几个方面:
1)ConfigServer(配置服务器)
Config Server 是一个配置管理服务器,负责从各种后端存储(如Git、SVN、本地文件系统等)中拉取配置信息,并提供REST API供客户端使用;
2)ConfigClient(配置客户端)
Config Client 是应用程序中一个组件,它允许应用程序通过ConfigClient连接到ConfigServer并动
态获取配置信息。客户端可以根据环境,服务名等动态选择对应的配置文件;
3)版本控制集成
Spring Cloud Config 默认使用Git作为配置存储的后端,这样可以利用Git的版本控制功能来管理配置文件的版本。每个环境对应⼀个特定的版本,可以通过切换版本号来自动获取对应环境下的配置。

二.Config Server
1)首先创建一个SpringBoot项目,添加依赖:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2)在项目启动类上添加 @EnableConfigServer 注解:
java
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
3)创建一个Git仓库:

并将配置信息上传到仓库中:
路径:config/config-server-dev.yml
java
data:
env: config-dev
user:
username: config-dev
password: config-dev
4)修改配置文件:
java
server:
port: 7071
spring:
application:
name: config-server # 应用名称
cloud:
config:
server:
git:
uri: https://gitee.com/???/config-server.git #配置文件Git 地址
default-label: master #配置文件分支
search-paths: config #配置文件所在根目录
5)Spring Cloud Config 有一套访问规则:
|---------------------------------------------|
| /{application}/{profile}[/{label}] |
| /{application}-{profile}.yml |
| /{label}/{application}-{profile}.yml |
| /{application}-{profile}.properties |
| /{label}/{application}-{profile}.properties |
{application} :表示微服务的名称,对应于配置中的 spring.application.name 属性;
{profile} :表示当前环境的配置文件,如dev、test、prod等,对应于 spring.profiles.active 属性;
{label}:表示Git仓库中的分支,标签或提交ID。这个参数是可选的,如果省略,默认会使用 mater分支。{label} 对于回滚到以前的配置版本非常有用。
使用下面的地址进行测试:127.0.0.1:7071/config-server-dev.yml

三.Config Client
Config Client 是应用程序中一个组件,它允许应用程序通过ConfigClient连接到ConfigServer并动
态获取配置信息。客户端可以根据环境,服务名等动态选择对应的配置文件。
1)添加依赖
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
2)配置文件
bootstrap.yml:
bash
spring:
profiles:
active: dev
application:
name: product-service
cloud:
config:
uri: http://127.0.0.1:7071 # 指定配置服务端的地址
Bootstrap属性具有高优先级,也就是说在的配置会优先于 bootstrap.yml 或bootstrap.properties 中定义application.yml 或 application.properties 中的配置。
bootstrap.yml 主要用于配置应用启动时所需的外部依赖和环境,而 application.yml 用于业务逻辑相关的配置(如数据库连接等)。
3)给Git仓库也写入配置文件
文件名:product-service-dev.yml
bash
data:
env: product-service-dev
4)读取配置
写个测试接口:
java
@RestController
@RequestMapping("/config")
public class ConfigController {
@Value("${data.env}")
private String env;
@RequestMapping("/getEnv")
public String getEnv(){
return "data.env:" + env;
}
}
访问这个接口:127.0.0.1:9090/config/getEnv

5)多平台配置支持
上面配置文件中是只有一个配置中心,所有的配置文件中都放在那里。现在有多个配置中心,那我们就不能采用之前的方法了,而是要用下面的方法:
bash
spring:
profiles:
active: prod
application:
name: product-service
#多环境配置
---
spring:
config:
activate:
on-profile: dev
cloud:
config:
uri: http://127.0.0.1:7071 # 指定配置服务端的地址
---
spring:
config:
activate:
on-profile: prod
cloud:
config:
uri: http://127.0.0.1:7072 # 指定配置服务端的地址
四.配置中心自动刷新
1.刷新机制
Spring Cloud Config 在项目启动时加载配置内容这一机制,导致了它存在一个缺陷,修改配置文件内容后,不会自动刷新。比如上面的项目,当服务启动之后,我们修改Gitee上的配置,新的配置并不会被加载。
Spring Cloud Config问我们提供了一个刷新机制。但是这个刷新是要手动执行的,不是完全自动刷新,只是不用重新启动项目罢了。
1)引入依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2)添加 @RefreshScope 注解
java
@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {
@Value("${data.env}")
private String env;
@RequestMapping("/getEnv")
public String getEnv(){
return "data.env:" + env;
}
}
3)开启端点
java
#需要开启的端点, 这里主要用到的是refresh端点, 只开启这一个就可以, 为了方便, 可以开启所有端点, 除了shutdown端点
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*"
4)测试
先访问127.0.0.1:9090/config/getEnv查看现在的内容:

此时我们需要手动调用http://127.0.0.1:9090/actuator/refresh这个接口,调用完成后会刷新配置:

注意,这里有一个细节是,调用的refresh这个接口必须是POST请求,GET不行。
2.WebHook
使用上面的方法刷新并不是真正实现的自动刷新,还要手动调用。如果我们想要实现完全的自动刷新,可以使用WebHook。Gitee 就提供了一种WebHook的方式,当有代码变更的时候,会调用我们设置的地址,来实现我们想达到的目的。


这里注意,输入的URL必须是公网IP,如果用127.0.0.1是不行的。
解决这个问题我们可以使用内网穿透技术,常用的软件有 ngrok 和 cpolar,这里拿cpolar来实现。cpolar怎么用很简单,直接去官网看看就行了。
将通过内网穿透得到的URL输入上即可。添加WebHook后,当有代码变更的时候,会调用我们设置的地址,以实现自动刷新。
经过测试发现,压根就没有用。原因是webhook发送post的时候会携带其他的信息。
因此我们要使用**过滤器(Filter)**去过滤这些信息。
java
@Component
public class WebHooksFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String url = new String(httpServletRequest.getRequestURI());
//检查url是否以/refresh结尾, 如果不是, 直接把请求传递给下一个过滤器或者目标资源
if(!url.endsWith("/refresh")){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
private class RequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest request) {
super(request);
}
//重写了HttpServletRequestWrapper的getInputStream方法, 返回了一个空的字节数组的ByteArrayInputStream
@Override
public ServletInputStream getInputStream() throws IOException {
byte[] bytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.read() == -1 ? true : false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
}
}
3.Spring Cloud Bus 自动刷新
如果说此时我们启动了多个服务,这多个服务都要进行配置刷新,那就要每一个服务配一个WebHook。
Spring Cloud Bus是SpringCloud体系中的⼀个组件,主要用于在集群环境中传播分布式系统的配置变更,以及提供事件驱动的通信机制。SpringCloudBus核心原理其实就是利用消息队列做广播,目前SpringCloudBus支持两种消息代理:RabbitMQ和Kafka。
在配置文件中加入MQ相关配置,再引入依赖:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
此时访问:localhost:9090/actuator/busrefresh(POST请求)即可同时刷新多个服务。
五.加密解密
在微服务开发中,配置文件可能包含一些敏感信息,比如数据库密码、API密码等,直接明文存储这些信息在配置文件中是非常危险的,尤其是当配置文件存储在版本控制系统(如Git)中时。这时候我们就需要对这些敏感信息进行加密。
针对这个问题,SpringCloudConfig提供了对属性进行加密解密的功能,以保护配置文件中的敏感数据不被泄露。
1.对称加密
1)添加密钥
在Config Server中添加 bootstrap.yml 设置密钥:
XML
#对称加密
encrypt:
key: 114514
2)添加bootstrap依赖:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
3)调用接口
调用 EncryptionController 的 encrypt 和 decrypt 接口进行加密和解密。


4)具体应用
前面我们在Gitee上面添加了一些 .yml 文件,现在在文件中添加password部分:
XML
data:
env: product-service-prod
password: '{cipher}edf62f27bc6dbc7655f1acb810003eef2004d0e4bc0a3bc193b451cdd7b5648d'
在加密结果前添加{cipher},会将其解密,通过HTTP发给客户端。
这个时候我们接收到的配置就是解密过的了。
2.非对称加密
1)生成密钥
非对称加密要生成密钥对,密钥的生成我们可以使用JDK中自带的 keytool。
keytool 是Java自带的数字证书管理工具,keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中,位置在**%JAVA_HOME%\bin\keytool.exe**:

直接使用命令:
XML
keytool -genkeypair -keystore D:/config-server.keystore -alias config-server -keyalg RSA -keypass config -storepass config
|---------------------|-------------------|
| -genkeypair | 表示生成密钥对 |
| -keystore | 指定密钥库的位置和名称 |
| -alias | 表示 keystore 关联的别名 |
| -keyalg | 表示指定密钥生成的算法 |
| -keypass -storepass | 密钥库口令和密钥口令 |
执行完命令后会生成一个 .keystore 文件,将这个文件放到Config Server 的resources包下。
2)添加配置
在 bootstrap.yml 添加配置:
XML
#非对称加密配置
encrypt:
key-store:
location: config-server.keystore #keystore文件存储路径
alias: config-server #密钥别名
password: config #storepass密钥仓库
secret: config #keypass 用来保护所生成密钥对中的密钥
剩下的操作与对称加密一样了。
六.总结
最后做个总结。
Spring Cloud Config的作用就是配置管理,说到配置管理,就不得不想到Nacos中的配置中心。两者的区别:
1)配置存储方面:Spring Cloud Config是依赖外部版本控制系统(比如Git),而Nacos使用外部的数据库(比如MySQL)。
2)获取配置和刷新配置:对于Spring Cloud Config,客户端在ConfigServer启动时拉取配置,但是当配置更改的时候客户端接收的配置不会自动刷新,要想自动刷新,要使用Spring Cloud Bus加上消息队列(RabbitMQ)才行。
对于Nacos:客户端支持两种获取方式:拉取模式(主动轮询,可配置间隔);推送模式(服务端配置变更后主动推送,实时性更高),Nacos原生支持动态刷新。
3)多环境管理:对于Spring Cloud Config支持划分目录;对于Nacos,通过内置的命名空间和配置组进行隔离。