【Spring Cloud】Spring Cloud Config

一.介绍

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.ymlbootstrap.properties 中定义application.ymlapplication.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是不行的。

解决这个问题我们可以使用内网穿透技术,常用的软件有 ngrokcpolar,这里拿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,通过内置的命名空间和配置组进行隔离。

相关推荐
百***359419 小时前
【Java EE】Spring请求如何传递参数详解
spring·java-ee·lua
懂得节能嘛.19 小时前
【SDK开发实践】从Java编码到阿里云制品仓库部署
java·阿里云·maven
空空kkk19 小时前
SpringMVC——异常
java·前端·javascript
重整旗鼓~19 小时前
1.大模型使用
java·语言模型·langchain
老友@19 小时前
深入 Spring AI:架构与应用
人工智能·spring·ai·架构
sino爱学习19 小时前
FastUtil 高性能集合最佳实践:让你的 Java 程序真正“快”起来
java·后端
.豆鲨包19 小时前
【Android】 View事件分发机制源码分析
android·java
北京地铁1号线20 小时前
数据结构:堆
java·数据结构·算法
百***864620 小时前
Spring Boot应用关闭分析
java·spring boot·后端
tanxiaomi20 小时前
Spring、Spring MVC 和 Spring Boot ,mybatis 相关面试题
java·开发语言·mybatis