【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,通过内置的命名空间和配置组进行隔离。

相关推荐
左左右右左右摇晃9 小时前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
孫治AllenSun9 小时前
【JVM】四大引用类型分析
java·开发语言·jvm
于慨9 小时前
安装java
java
夕除9 小时前
sign 是什么
java·前端
Penina101810 小时前
代码如何导入idea
java·intellij idea
.Hypocritical.10 小时前
Maven笔记——2
java·笔记·maven
huohuopro10 小时前
手写 Tomcat 步骤教程
java·网络
snow@li10 小时前
Java:Lombok 完整讲解
java
Shadow(⊙o⊙)10 小时前
高并发内存池:Part-1——定长内存池
java·前端·javascript
国服第二切图仔10 小时前
17-config命令 - 配置管理系统
java·前端·javascript