Spring Cloud 配置中心
Spring Cloud 配置中心
- 一.Spring Cloud Config 介绍
- 1.基本介绍
- 2.核⼼概念
- 二.Config Server
- 1.创建⼀个Spring Boot项⽬
- 2.添加依赖
- 3.启⽤config server
- 4.完善配置
- 5.初始化Git仓库
- 6.测试
- 三.Config Client
- 1.配置管理
- 2.添加依赖
- 3. 配置⽂件
- 4.读取配置
- 5.多平台配置⽀持
- 四.配置中心自动刷新
- 1.添加依赖
- 2.添加 @RefreshScope
- 3.开启端点
- 4.⼿动刷新
- 5.添加Webhook(了解)
- 五.Spring Cloud Bus ⾃动刷新
- 1.问题
- 2.解决办法
- 1)添加配置
- 2)添加依赖
- 3)刷新配置
- 六.Spring Cloud Config 加密解密
- 1.介绍
- 2.对称加密
- 1)检查加密环境
- 2)加密解密演⽰
- 3)加密解密案例实现
- 3.⾮对称加密
- 1)⽣成密钥
- 2)加密解密演示
- 3)加密解密案例实现
一.Spring Cloud Config 介绍
1.基本介绍
在微服务架构中,每个微服务⼀般都有⾃⼰的配置⽂件,⽐如数据库连接,Redis,MQ等相关配置.在不同的环境下,这些配置都会不同。如果每个服务⾃⾏管理这些配置,容易导致以下问题:
- 维护成本⾼:每次配置修改,都需要重新部署服务.
- 版本问题:同⼀个应⽤的不同实例,需要使⽤同⼀个版本,逐⼀部署,可能会导致版本不⼀致的问题.
- 安全问题:⼀些敏感配置(如数据库密码) 可能会泄露,缺乏安全的管理⼿段。
所以,就有了配置中⼼。可以让我们对项⽬的配置进⾏集中管理。
Spring Cloud Config 是 Spring Cloud 家族中较早的配置中⼼,Spring Cloud Config 是分布式系统中,
为服务端和客⼾端解决配置管理的⽅案。
它提供了集中化的配置管理,使得在不同环境(开发,测试,⽣产) 中管理应⽤程序配置变得更加简单和⼀致.它⽀持配置的动态刷新,允许在不重启应⽤的情况下更新配置,提⾼了系统的灵活性和响应速度.
2.核⼼概念
Spring Cloud Config 是⼀个分布式配置管理系统,它主要包括以下⼏个⽅⾯:
- Config Server(配置服务器)
Config Server 是⼀个配置管理服务器,负责从各种后端存储(如Git、SVN、本地⽂件系统等)中拉取配置信息,并提供REST API供客户端使⽤。 - Config Client(配置客户端)
Config Client 是应⽤程序中⼀个组件,它允许应⽤程序通过Config Client连接到Config Server并动态获取配置信息。客户端可以根据环境,服务名等动态选择对应的配置⽂件. - 版本控制集成
Spring Cloud Config 默认使⽤Git作为配置存储的后端,这样可以利⽤Git的版本控制功能来管理配置⽂件的版本。每个环境对应⼀个特定的版本,可以通过切换版本号来⾃动获取对应环境下的配置。
二.Config Server
搭建Config Server的⽅式很简单,Spring Cloud Config Server是⼀个标准的Spring Boot应⽤程序,通过引⼊相应的依赖和注解即可快速启动.
主要分为以下⼏步:
- 创建项⽬
- 添加依赖
- 启⽤Config Server
- 完善配置
- 初始化Git仓库
1.创建⼀个Spring Boot项⽬
a. 创建空Maven项⽬config-server
b. 完善pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
c. 添加启动类
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
2.添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
3.启⽤config server
在启动类添加注解 @EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}
}
4.完善配置
server:port: 7071
spring:application:name: config-server # 应⽤名称cloud:config:server:git:uri: https://gitee.com/chenpinganxiansheng/config-server.git #配置⽂件Git 地址default-label: master #配置⽂件分⽀search-paths: config #配置⽂件所在根⽬录
5.初始化Git仓库
- 创建git仓库(github或者gitee都可以)
- 上传配置信息
6.测试
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。这个参数是可选的,如果省略,默认会使⽤ master分⽀。 {label} 对于回滚到以前的配置版本⾮常有⽤。
基于以上规则,我们可以访问以下地址,如果可以正常返回数据,则说明配置中⼼服务端⼀切正常
http://127.0.0.1:7071/config-server/dev
http://127.0.0.1:7071/config-server/prod
http://127.0.0.1:7071/config-server-dev.yml
http://127.0.0.1:7071/config-server-prod.yml
三.Config Client
Config Client 简单来说,就是应用程序连接到Config Server并动态获取配置信息。客户端的配置相对简单,只需引⼊相关依赖并配置好配置服务端的地址.
1.配置管理
在git仓库添加配置
product-service-dev.yml
data:env: product-service-dev
product-service-prod.yml
data:env: product-service-prod
重启Config Server,访问http://127.0.0.1:7071/product-service-dev.yml,观察配置是否设置成功
2.添加依赖
Spring Cloud 会创建⼀个 Bootstrap Context,作为Spring 应⽤"Application Context" 的父上下文,在Spring应⽤启动的初始化阶段, Bootstrap Context 负责从外部源(Consul)加载配置属性并解析配置。
所以除了添加 spring-cloud-starter-config 依赖之外,还需要加⼊依赖 spring-cloudstarter-bootstrap 来实现
<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>
3. 配置⽂件
Bootstrap属性具有⾼优先级,也就是说在 bootstrap.yml 或 bootstrap.properties 中定义
的配置会优先于 application.yml 或 application.properties 中的配置。
bootstrap.yml 主要⽤于配置应⽤启动时所需的外部依赖和环境,而 application.yml 用于业务逻辑相关的配置(如数据库连接等)
bootstrap.yml
spring:profiles:active: devapplication:name: product-servicecloud:config:uri: http://127.0.0.1:7071 # 指定配置服务端的地址# profile: dev
也可以通过 spring.cloud.config.profile: dev 来指定⽂件.如果同时存在,以spring.profiles.active 配置为主.
4.读取配置
写测试接⼝
@RequestMapping("/config")
@RestController
public class ConfigController {@Value("${data.env}")private String env;@RequestMapping("/getEnv")public String getEnv(){return "env:"+ env;}
}
启动服务,测试接⼝
http://127.0.0.1:9090/config/getEnv
5.多平台配置⽀持
bootstrap.yml
spring:profiles:active: prod#配置两个版本的配置,并通过 spring.profiles.active 设置当前使⽤的版本
---
spring:config:activate:on-profile: prodapplication:name: product-servicecloud:config:uri: http://localhost:7071
---
spring:config:activate:on-profile: devapplication:name: product-servicecloud:config:uri: http://localhost:7071
修改 spring.profiles.active的值,观察配置结果变化
四.配置中心自动刷新
Spring Cloud Config 在项⽬启动时加载配置内容这⼀机制,导致了它存在⼀个缺陷,修改配置⽂件内容后,不会⾃动刷新。⽐如上⾯的项⽬,当服务启动之后,我们修改github上的配置,新的配置并不会被加载。Spring Cloud Config提供了⼀个刷新机制,但是需要我们主动触发。
接下来看下如何操作.
1.添加依赖
⾃动刷新机制,需要借助 Actuator提供的功能,所以需要添加 spring-boot-starter-actuator依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
spring-boot-starter-actuator 是 Spring Boot 提供的⼀个强⼤的监控和管理⼯具,它允许开发者查看和管理 Spring Boot 应⽤的各种运⾏时指标和状态.核⼼功能有健康检查,收集和展⽰应⽤程序的运⾏指标,以及端⼝配置
/actuator/refresh 端点是 Spring Cloud Actuator 提供的⼀个功能,它允许在运行时动态刷新Spring Cloud Config客户端的配置。
2.添加 @RefreshScope
@RefreshScope
@RequestMapping("/config")
@RestController
public class ConfigController {@Value("${data.env}")private String env;@RequestMapping("/getEnv")public String getEnv(){return "env:"+ env;}
}
3.开启端点
在application.yml开启端点
#需要开启的端点, 这⾥主要⽤到的是refresh端点, 只开启这⼀个就可以, 为了⽅便, 可以开启所
有端点, 除了shutdown端点
management:endpoint:shutdown:enabled: false
endpoints:web:exposure:include: "*"
4.⼿动刷新
启动服务,访问 http://127.0.0.1:9090/config/getEnv修改配置后,发现配置并不能⽴即⽣效需要⼿动调⽤⼀下接 http://127.0.0.1:9090/actuator/refresh (POST请求)动态刷新 Spring Cloud Config 客户端的配置(客户端服务接⼝)
再次访问,发现配置发⽣变化.
5.添加Webhook(了解)
虽然上述操作可以让我们的配置得到⽣效,但是每次修改都需要⼿动访问refresh接⼝,这样也很不⽅便。Gitee 提供了⼀种 webhook 的⽅式,当有代码变更的时候,会调⽤我们设置的地址,来实现我们想达到的⽬的.
参考:添加WebHook - Gitee.com
GitHub 也有类似的配置
选择配置的仓库代码中,点击 管理 -> WebHooks -> 添加webHook
此处的URL需要为⼀个域名,在没有公⽹和域名的情况下,我们可以采⽤内⽹穿透的技术,暴露给外⽹实现内⽹穿透,常⻅的⽅法是使⽤第三⽅⼯具或服务。如ngrok,cpolar
cpolar操作
ngrok操作
具体操作可参考:2⾏代码将你的本地服务暴露在公⽹!-阿⾥云开发者社区链接失效的话,可以自行搜索.
Filter 介绍
webhook发送post的时候会携带其他的信息,可以通过过滤器把对应的多余信息去掉.
在Web开发中,过滤器(Filter)和拦截器(Interceptor)都是处理请求和响应的组件,但它们在框架中的作⽤和使⽤场景有所不同.
⼆者区别如下:
- 过滤器是JavaServlet规范的⼀部分,这意味着任何遵循该规范的Web服务器(如Tomcat、Jetty等)都可以使⽤过滤器,拦截器是特定于框架的.
- 过滤器在请求到达Servlet之前和响应发送回客户端之后都可以进⾏处理,拦截器主要在请求到达控制器(Controller)之前和之后处理,但不在响应发送回客户端之后处理.
- 过滤器可以应⽤于所有的请求,包括静态资源和动态资源,拦截器通常只作⽤于动态资源,即那些
由框架的控制器处理的请求.- 过滤器通过实现 javax.servlet.Filter 接⼝来创建,拦截器通过实现特定框架的接⼝(如
Spring的 HandlerInterceptor )来创建。
过滤器和拦截器在Spring Boot中的核⼼区别在于执⾏时机,应⽤场景及使⽤便捷性.过滤器适合系统级通⽤逻辑处理(如数据压缩、编码设置),⽽拦截器位于控制器层⾯,更适合业务逻辑扩展(如权限校验,⽇志记录),虽然看起来很复杂,但是过滤器和拦截器的设计初衷是将请求的前置处理和响应的后置处理从业务代码中分离出来,作为通⽤处理逻辑供开发者扩展实现。本质都是处理请求参数或者响应结果。在实际开发中,选择使⽤过滤器还是拦截器通常取决于具体的框架和需求。
通过过滤器把请求中对应的多余信息去掉
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@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 = newRequestWrapper(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;
}
}
}
启动程序,通过修改配置⽂件,发现应⽤程序中的配置可以得到实时更新。
五.Spring Cloud Bus ⾃动刷新
1.问题
启动多个服务,修改配置,会发现只有⼀个配置webHook刷新url的服务⽣效,如果需要其他服务⽣效,需要再调⽤其他服务的刷新接
-
启动多个服务
-
修改配置⽂件
-
测试多个服务获取配置⽂件的内容
结果发现只有配置webHook刷新url的服务⽣效,如果需要其他服务⽣效,需要再调⽤其他服务的刷新接⼝.
2.解决办法
如果服务⽐较多,需要每个服务都调⽤⼀次才能⽣效,可以借助SpringCloudBus来解决。
Spring Cloud Bus 是Spring Cloud体系中的⼀个组件,主要⽤于在集群环境中传播分布式系统的配置变更,以及提供事件驱动的通信机制。Spring Cloud Bus 核⼼原理其实就是利⽤消息队列做⼴播,所以要先有个消息队列,⽬前Spring Cloud Bus ⽀持两种消息代理:RabbitMQ和Kafka。
我们采⽤RabbitMQ实现。
1)添加配置
修改为对应的IP,账号,密码和vhost
spring:rabbitmq:addresses: amqp://账号:密码@云服务器IP:5672/虚拟机
2)添加依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
3)刷新配置
启动服务,修改配置,刷新任⼀节点。http://localhost:9090/actuator/busrefresh post⽅法 ,所有节点全部⽣效了,webHook刷新的url可以修改为 /actuator/busrefresh
六.Spring Cloud Config 加密解密
1.介绍
在微服务开发中,配置文件可能包含⼀些敏感信息,⽐如数据库密码,API密码等,直接明⽂存储这些信息在配置⽂件中是⾮常危险的,尤其是当配置⽂件存储在版本控制系统(如Git)中时。这时候我们就需要对这些敏感信息进⾏加密。
针对这个问题,Spring Cloud Config提供了对属性进⾏加密解密的功能,以保护配置⽂件中的敏感数据不被泄露。
⽐如这个配置:
data:password:
'{cipher}edf62f27bc6dbc7655f1acb810003eef2004d0e4bc0a3bc193b451cdd7b5648d'
密码算法分类
密码算法主要分为三类:对称密码算法,⾮对称密码算法,摘要算法
- 对称密码算法 是指加密秘钥和解密秘钥相同的密码算法。常⻅的对称密码算法有:AES,DES,3DES,RC4,RC5,RC6 等。
- ⾮对称密码算法 是指加密秘钥和解密秘钥不同的密码算法。该算法使⽤⼀个秘钥进⾏加密,⽤另外⼀个秘钥进⾏解密。
- 加密秘钥可以公开,⼜称为公钥
- 解密秘钥必须保密,⼜称为私钥
常见的非对称密码算法有:RSA,DSA,ECDSA,ECC 等
- 摘要算法 是指把任意⻓度的输⼊消息数据转化为固定⻓度的输出数据的⼀种密码算法。摘要算法是不可逆的,也就是⽆法解密。通常⽤来检验数据的完整性的重要技术,即对数据进行哈希计算然后⽐较摘要值,判断是否⼀致。常见的摘要算法有:MD5,SHA系列(SHA1,SHA2等),CRC(CRC8,CRC16,CRC32)
2.对称加密
1)检查加密环境
- 下载jar包
Java 中提供了⼀套⽤于实现加密,密钥⽣成等功能的包JCE(Java Cryptography Extension),这些包提供了对称,⾮对称等加密⽀持,但是默认的 JCE 是⼀个有限⻓度的 JCE,我们需要到 Oracle 官⽹去下载⼀个不限⻓度的 JCE 。
下载地址
下载之后,共有3个文件
我们需要将 local_policy.jar 和 US_export_policy.jar 两个⽂件复制到
$JAVA_HOME/jre/lib/security ⽬录下,如果之前⽬录存在同名jar包,则覆盖即可。
在jdk9之后就不存在jre目录了,此时放在 $JAVA_HOME/jdk-17\conf\security
- 添加配置
在Config Server服务中添加bootstrap.yml⽂件,设置密钥
encrypt:key: muhuoyin666
- 添加bootstrap依赖
config server v3.0.0以上时,bootstrap.yml⽂件不会加载,需要引⼊bootstrap相关的依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
-
测试
访问:http://127.0.0.1:7071/encrypt/status
出现以下结果,就表示我们的加密环境配置是正确的 -
常见问题
- 访问时出现 “The encryption algorithm is not strong enough”
原因是未配置密钥
- 访问时出现"java.lang.UnsupportedOperationException:Noencryption for FailsafeTextEncryptor." 原因是未添加 spring-cloud-starter-bootstrap 依赖
2)加密解密演⽰
加密环境准备完成之后,就可以尝试访问下 /encrypt 和 /decrypt 来使⽤加密解密功能 源码实现在: EncryptionController 通过cmd,来观察加密解密
也可以通过postman来观察加密解密
加密:
3)加密解密案例实现
通过Config Server 可以对数据进⾏加密,那么在Config Client中,就可以使⽤加密后的数据进⾏传输了.
把加密后的数据,更新到git配置⽂件中,并在加密结果前添加 {cipher} ,如果远程属性源包含加密的内容(以 {cipher} 开头),则将其解密,通过Http发送给客户端。
- 修改配置
data:env: product-service-prodpassword:
'{cipher}edf62f27bc6dbc7655f1acb810003eef2004d0e4bc0a3bc193b451cdd7b5648d'
- 读取配置
在ConfigController 添加代码
@Value("${data.password}")
private String password;
@RequestMapping("/getPassword")
public String getPassword(){
return "password:"+ password;
}
- 测试
访问接⼝:http://127.0.0.1:9090/config/getPassword
发现客户端已完成解密操作
3.⾮对称加密
Spring Cloud Config 不仅可以使⽤对称性加密,也可以使⽤⾮对称性加密(⽐如 RSA 密钥对)。虽然⾮对称性加密的密钥⽣成与配置相对复杂⼀些,但是它具有更⾼的安全性。
1)⽣成密钥
⾮对称加密要求我们先⽣成密钥对,密钥的⽣成我们可以使⽤ JDK 中⾃带的 keytool。
keytool 是⼀个 Java ⾃带的数字证书管理⼯具,keytool 将密钥(key)和证书 (certificates)存在⼀个称为 keystore 的⽂件中。它的位置在: %JAVA_HOME%\bin\keytool.exe ,可以直接使⽤cmd来调⽤打开cmd,输⼊命令:
keytool -genkeypair -keystore D:/config-server.keystore -alias config-server
-keyalg RSA -keypass config -storepass config
- genkeypair 表⽰⽣成密钥对
- alias 表示 keystore 关联的别名
- keyalg 表示指定密钥⽣成的算法
- keystore 指定密钥库的位置和名称
- keypass -storepass :密钥库⼝令和密钥⼝令执⾏过程中,其它的信息可以输⼊也可以直接回⻋表⽰Unknown,⾃⼰做练习⽆所谓,实际开发中还是建议如实填写。命令执⾏完成后,在D盘 路径下就会⽣成⼀个名为 config-server.keystore 的⽂件
执⾏结果如下:
2)加密解密演示
- ⽣成密钥之后,把密钥文件放在Config Server中
2. 添加非对称加密的相关配置
bootstrap.yml
需要注掉对称加密时的相关配置
#encrypt:
# key: bite666
encrypt:key-store:location: config-server.keystore #keystore⽂件存储路径alias: config-server #密钥别名password: config #storepass密钥仓库secret: config #keypass ⽤来保护所⽣成密钥对中的密钥
- 启动Config Server
加密解密的观察和对称加密⼀样,可以通过cmd观察,也可以通过postman观察 cmd
Postman
加密:
解密:
3)加密解密案例实现
对于Config Client ⽽⾔,对称加密和⾮对称加密的使⽤也是⼀样的。修改配置为⾮对称加密后的密文
data:env: product-service-prodpassword:
'{cipher}AQCspOSxPyv6OGU7qbDiddUyzxx+PCgOCE4AcIpf+/2C+LKRmIm6OMt//4lLJOpv867xfQZsa8EQvTWD3EKQWBsIoVFZ39hbYT5krKRnxaZLR5vb/mEaKWSe6xaIm6nWu/LB4Bc9cbwveRKZCHqRHSGBr9R0aUnNL+IXagMQaWJf6m1H8CyYxCDRKA8H07ACu707GNsKIE9XPMh3+8rN5e/b1ZK6jagqENUTsfZJJQd+WPfx5utC3SRA8ct3OGgSInP6gkTDwHQ9MfVCMI1WTFhV288ByNW3DVjJgSxCYQZUflnJsfjOnQ48wuCH+dGSOTgrHOF3UmtnodGutLc3NkKV9qrtpOBd4dzHLXR+ENd2YVuiFtYnQyBuwQ4mfXWp990='
访问接⼝:http://127.0.0.1:9090/config/getPassword