Springboot监听ConfigMap配置文件自动更新配置

news/2025/1/15 21:57:07/

背景:

最近调研使用k8s的ConfigMap来作为springboot项目的配置中心,需要实现热更新机制,避免pod重启影响业务。

ConfigMap作为挂载卷使用的时候可以更新pod中的配置内容,但是业务应用需要能监听并处理这些变更。我在测试的时候已经可以看到pod中的ConfigMap配置更新了,但是业务应用始终没有刷新配置。参考网上各位大神的关于spring-cloud-starter-kubernetes-config的配置,一直未能实现业务配置热更新k8s在v1.19之后已经改为其他方式了,其他开源方案过于复杂,遂改换思路,简单点,就用java最原始的文件变更监听来手动刷新配置。

相关环境:

macOS: bigsur 11.7.8
docker desktop: 4.22.0 
docker engine: 24.0.5
kubernetes: 1.27.2
openjdk: 17.0.2
spring-boot:2.7.10
spring-cloud-context:3.1.1

实现:

1、引入依赖

主要依赖common-io对文件的监听和springcloud刷新上下文

<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.16.0</version>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-context</artifactId><version>3.1.1</version>
</dependency>

完整依赖如下 

<modelVersion>4.0.0</modelVersion>
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.10</version>
</parent>
<groupId>com.example</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>test</description>
<properties><java.version>17</java.version>
</properties>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator-autoconfigure</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.16.0</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-context</artifactId><version>3.1.1</version></dependency>
</dependencies>

2、文件监听器

在项目启动后开启监听

java">import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;import java.io.File;@Slf4j
@Service
public class ConfigMapFileMonitor implements CommandLineRunner {@Autowiredprivate ConfigMapFileListener configFileListener;@Overridepublic void run(String... args) throws Exception {log.info("启动configMap文件监听...");// configMap挂载路径mountPathString fileDir = "/app/config/";FileAlterationMonitor monitor = new FileAlterationMonitor(1000);FileAlterationObserver observer = new FileAlterationObserver(new File(fileDir));observer.addListener(configFileListener);monitor.addObserver(observer);monitor.start();log.info("configMap文件监听开始...");}}

3、文件变更处理逻辑

主要用到Springcloud的ContextRefresher.refresh()方法,可能有的配置不需要更新,这里就需要根据实际业务逻辑来决定要更新哪些配置了。

java">import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.stereotype.Service;import java.io.File;
import java.util.concurrent.Executors;@Slf4j
@Service
public class ConfigMapFileListener extends FileAlterationListenerAdaptor {@Qualifier("configDataContextRefresher")@Autowiredprivate ContextRefresher contextRefresher;@Overridepublic void onFileChange(File file) {log.info("configMap文件变更,异步刷新上下文...");Executors.newSingleThreadExecutor().execute(() -> {contextRefresher.refresh();log.info("异步刷新上下文完成。");});}
}

4、配置类

需要更新的配置使用配置类加@RefreshScope注解,@Value的方式无法直接更新

java">import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;@Data
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
public class DatabaseConfig {private String url;private String username;private String password;
}

类似以下方式的配置无法直接更新,可能需要增加一些逻辑,自行处理吧。。。

java">@Value("${dfs.console.server}")
private String dfsConsoleServer;

补充下,刷新配置不是修改后立即执行的,是有时间间隔的,可以配置,自行研究吧

其他

如果在运行过程中遇到如下错误,需要在k8s中添加相应权限:

o.s.cloud.kubernetes.StandardPodUtils    : Failed to get pod with name:[sdk-test-7b9dd4f586-69vql]. You should look into this if things aren't working as you expect. Are you missing serviceaccount permissions?
io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: GET at: https://10.96.0.1/api/v1/namespaces/default/pods/sdk-test-7b9dd4f586-69vql. Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. pods "sdk-test-7b9dd4f586-69vql" is forbidden: User "system:serviceaccount:default:default" cannot get resource "pods" in API group "" in the namespace "default".at io.fabric8.kubernetes.client.dsl.base.OperationSupport.requestFailure(OperationSupport.java:570) ~[kubernetes-client-4.13.2.jar:na]

 使用如下代码可以监听到配置变更事件,可以针对具体业务看看有没有用吧。

java">@EventListener
public void envListener(EnvironmentChangeEvent event) {System.out.println("conf change: " + event);
}

参考资料:

spring-cloud-kubernetes 实战 二 configmap_spring-cloud-starter-kubernetes-config github-CSDN博客

spring-cloud-kubernetes自动同步k8sconfigmap更新_fabric8 更新configmap-CSDN博客

Spring boot实现更改配置文件后自动更新配置-CSDN博客

SpringBoot基础篇配置信息之配置刷新-腾讯云开发者社区-腾讯云 (tencent.com)


http://www.ppmy.cn/news/1458780.html

相关文章

Leetcode—622. 设计循环队列【中等】

2024每日刷题&#xff08;128&#xff09; Leetcode—622. 设计循环队列 实现代码 class MyCircularQueue { public:MyCircularQueue(int k): q(k) {qSize k;}bool enQueue(int value) {if(isFull()) {return false;}q[rear] value;rear (rear 1) % qSize;deflag false;…

Linux中gcc/g++的使用

文章目录 前言gcc/g 前言 gcc和g即为编译器。其中gcc为c语言的编译器&#xff0c;只能编译c语言&#xff1b;g为c的编译器&#xff0c;既能编译c语言&#xff0c;又能编译c。 在前面的文章中&#xff0c;我们提到代码转换成可执行程序需要经过 预处理&#xff08;进行宏替换)…

php使用Canal监听msyql

canal需要java8 去官网下载java8 安装JAVA #创建目录 mkdir -p /usr/local/java/ #解压到目录 tar zxvf jdk-8u411-linux-x64.tar.gz -C /usr/local/java/配置环境变量在 /etc/profile 最后加入 export JAVA_HOME/usr/local/java/jdk1.8.0_411 export CLASSPATH.:$JAVA_HOM…

NFS共享存储服务配置实践

一、NFS 1.NFS定义 NFS&#xff08;Network File System&#xff09;网络文件服务&#xff1a;基于TCP/IP传输的网络文件系统协议&#xff0c;NFS服务的实现依赖于RPC&#xff08;Remote Process Call&#xff09;远端过程调用&#xff1a;通过使用NFS协议&#xff0c;客户机…

【设计模式】单例模式的前世今生

文章目录 引言简介起航&#xff01;向“确保某个类在系统中只有一个实例”进发 ⛵️Lazy SingletonDouble-checked locking&#xff08;DCL&#xff09; SingletonVolatile SingletonAtomic SingletonMeyers Singleton 附&#xff1a;C静态对象的初始化 引言 说起单例模式&…

校友录系统的设计与开发

**中文摘要&#xff1a;**随着互联网技术的不断发展和普及&#xff0c;人们对于信息化、数字化的需求也越来越高。在此背景下&#xff0c;校友录系统的设计与开发显得尤为重要。本文旨在设计和开发一款方便实用的校友录系统&#xff0c;为学校提供一个联系和管理校友的平台&…

设计模式——建造者模式(Builder)

建造者模式&#xff08;Builder Pattern&#xff09;是一种对象构建的设计模式&#xff0c;它允许你以一种逐步构建的方式来创建复杂对象。建造者模式通常用于构建具有多个组成部分的对象&#xff0c;并且这些组成部分通常具有不同的构建和表示方式。 建造者模式主要包括以下几…

Java毕设之基于springboot的医护人员排班系统

运行环境 开发语言:java 框架:springboot&#xff0c;vue JDK版本:JDK1.8 数据库:mysql5.7(推荐5.7&#xff0c;8.0也可以) 数据库工具:Navicat11 开发软件:idea/eclipse(推荐idea) 系统详细实现 医护类型管理 医护人员排班系统的系统管理员可以对医护类型添加修改删除以及…