欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新

程序员文章站 2022-06-12 23:16:00
...

接上篇《38.Spring Cloud Config 与Eureka配合使用》  Spring Cloud版本为Finchley.SR2版

上两篇我们讲解了有关Spring Cloud Config与Eureka的配合使用,本篇我们来讲解Spring Cloud Config是如何来刷新其配置属性的。

本部分官方文档:https://cloud.spring.io/spring-cloud-static/Finchley.SR4/single/spring-cloud.html#refresh-scope
注:好像Finchley.SR2的文档已经挂了,最新的是Finchley.SR4的文档。

一、动态刷新属性的需求

Spring Cloud Config的配置刷新是指其配置属性的刷新。例如之前我们Config Client中获取profile属性的代码:

package com.microserver.cloud;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    
    @Value("${type}")
    private String profileType;
    
    @GetMapping("/profileType")
    private String getProfileType() {
        return this.profileType;
    }
} 

这里我们配置文件中spring.cloud.config.profile配置的是dev,而Config Client的应用名(spring.application.name)为microserver-config-client,所以在远端的gitee仓库中,会获取到名为“microserver-config-client-dev.yml”文件中的type属性的值“client-dev”。

如果我们有一天把gitee仓库中的“icroserver-config-client-dev.yml”文件中的type属性改为了其他值,但是我的系统目前运行在生产环境下,想要在不停机的情况下,刷新上面的profileType属性,使其获取新的type值,应该怎么做呢?

注:这种场景很常见,例如大型互联网公司的电商网站,在双十一或者其他购物高峰期的时候,访问量很大,此时我们有可能需要调整数据库连接池或一些其它配置(流控、连接池大小等)的性能参数,使其能够适应更高的访问量,此时我们也是不愿意重新服务来达到这个效果的。

二、实现配置刷新的手动刷新

1、具体实现
Spring Cloud Config是支持配置刷新的,我们下面来实现一个手动刷新属性的效果。
实现自动刷新效果,不需要修改Config Server的代码,只需要修改Config Client即可。
复制之前的microserver-config-client工程,更名为“microserver-config-client-refresh”:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新
然后我们修改ConfigClientController类:

package com.microserver.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    
    @Autowired 
    private Paramters paramters;
    
    @GetMapping("/profileType")
    private String getProfileType() {
        return paramters.getProfileType();
    }
} 

我们将profileType封装在了一个Paramters类中:

package com.microserver.cloud;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@Component
@RefreshScope
public class Paramters {
    @Value("${type}")
    private String profileType;

    public String getProfileType() {
        return profileType;
    }

    public void setProfileType(String profileType) {
        this.profileType = profileType;
    }
    
}

这里我们在Paramters类结构的上方新增了一个名为“@RefreshScope”的注解,该注解的作用我们测试完毕之后会详细讲解。
然后在pom.xml文件中添加actuator的依赖:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <artifactId>microserver-config-client-refresh</artifactId>
  <name>microserver-config-client-refresh</name>
  
  <parent>
        <groupId>com.microserver.cloud</groupId>
        <artifactId>microserver-spring-cloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
  
  <dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
  
</project>

对于actuator依赖大家肯定不陌生,它是可以帮助我们监控和管理Spring Boot应用的服务组件,提供健康检查、审计、统计和HTTP追踪等功能。

为什么要加actuator依赖呢?这里我们也留一个悬念。

然后更新application.yml文件,添加暴露refresh端点的配置:

server:
  port: 8091
spring:
  application:
    name: microserver-config-client
management:
  endpoints:
    web:
      exposure:
        include: refresh
    
type: abcd

注:在Spring Boot升级到2.0.3.RELEASE后需新增配置,之前不需要配置。

我们启动原来的microserver-config-server配置服务,和新写的microserver-config-client-refresh服务(先启动服务端,在启动客户端,否则参数获取不到):
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新

然后访问microserver-config-client-refresh服务的“profileType”节点,可以看到当前获取的还是之前的“client-dev”值:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新
此时我们在本地仓库,将“microserver-config-client-dev.yml”文件中的type属性修改为“client-dev-refresh”,并提交远程仓库:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新

【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新

然后此时就是手工刷新的重点了,我们打开postman工具,访问microserver-config-client-refresh服务的一个“refresh”服务,即“http://localhost:8091/actuator/refresh”:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新
可以看到返回的结果:

[
    "config.client.version",
    "type"
]

这里就是更新的相关的远程配置的名称。

我们看一下客户端控制台打印的日志:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新
可以看到客户端重新访问了Config Server的服务,重新进行了Fetching config动作,将相关的参数加载进来。

然后我们重新访问服务的“profileType”节点,可以看到当前获取的是修改后的“client-dev-refresh”值:
【Spring Cloud总结】39.Spring Cloud Config配置属性刷新之手动刷新

2、一些异常问题的解决
在上面的操作过程中,如果童鞋们不将profileType封装到Paramters类中,依然使用“return this.profileType”的方式获取profileType值,可能会遇到访问microserver-config-client-refresh服务的“profileType”节点,发现访问不到值的情况,而一旦拿掉@RefreshScope就可以获取到,这是怎么回事?为什么加了@RefreshScope注解后获取不到Value的值了呢?

我们目前使用的springboot和springcloud版本分别如下:
springboot 2.0.4
springcloud  Finchley.SR2

这极有可能是以上两个版本组合之后出现的一个Bug,这个版本中,将@RefreshScope放到controller层,任何在controller层注入的bean取值都为null,拿不到任何数据,也不报错。
目前查阅很多资料没有解决方案,只是参考了另一个博主的方法,按照解决方案1的做法实现了数据加载。
下面将两种解决方案提供给大家:
解决方案1:https://blog.csdn.net/soulsda/article/details/89642241
解决方案2:https://www.cnblogs.com/xiaoyao-001/p/11511595.html


三、手工刷新配置的原理

上面我们做了两点来实现手工刷新,一个是为注入配置参数的类添加了@RefreshScope注解,一个是添加了actuator的依赖,那么这两个操作对于手工刷新配置有什么作用呢?

1、@RefreshScope注解
首先我们来说一下@RefreshScope注解。我们在Java中创建的Bean,其中绑定了外部配置的参数,仅在首次访问时初始化。如果一个Bean添加了@RefreshScope注解,这个Bean中的参数就会进行强制懒加载。

而其原理就是,这个注解会给范围内的每个bean创建个代理对象,如果刷新bean,则下次访问bean时(即执行方法)将创建一个新实例。所有生命周期方法都应用于bean实例,因此在刷新时会调用在bean工厂的销毁回调方法,然后在创建新实例时正常调用初始化回调。从原始bean定义创建新的bean实例,因此在创建时会重新加载所有外部配置属性(属性占位符或字符串文字中的表达式)。

所以我们刷新了ConfigClientController类,此时会创建该bean的新的实例,并重新加载所有外部配置属性,我们的profileType参数绑定的“${type}”也会被重新加载,并绑定到profileType参数上。

2、actuator依赖
为什么要添加actuator依赖的依赖呢?我们中间使用了一个“refresh”的刷新服务,而这个刷新服务是由actuator组件提供的,该服务的作用就是用来重新加载bootstrap context以及刷新@RefreshScope注解标注的Bean。

四、手工刷新的弊端

我们是通过访问“refresh”的刷新服务去手工刷新配置的,如果我们的微服务的体量比较大,例如有几百个集群,每个集群有几十个端点需要刷新,我们需要给每个端点都需要用这种post的请求去刷新该端点,这种效率很低,而且时效性不高,在我们需要集体刷新配置时,是有问题的。所以这种情况下我们需要手工刷新的策略来解决此问题。

下一篇我们就来讲解如何对Spring Cloud Config进行自动刷新的操作。

参考:《51CTO学院Spring Cloud高级视频》
https://blog.csdn.net/haveqing/article/details/91414012

转载请注明出处:https://blog.csdn.net/acmman/article/details/106157061