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

背景:

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

ConfigMap作为挂载卷使用的时候可以更新pod中的配置内容,但是业务应用需要能监听并处理这些变更。我在测试的时候已经可以看到pod中的ConfigMap配置更新了,但是业务应用始终没有刷新配置。参考网上各位大神的关于spring-cloud-starter-kubernetes-config的配置,一直未能实现业务配置热更新,而且本身对k8s不是太熟悉,遂改换思路,用java最原始的文件变更监听来手动刷新配置。

实现:

1、引入依赖

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

XML 复制代码
<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>

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 {

    @Autowired
    private ConfigMapFileListener configFileListener;

    @Override
    public void run(String... args) throws Exception {
        log.info("启动configMap文件监听...");
        // configMap挂载路径mountPath
        String 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")
    @Autowired
    private ContextRefresher contextRefresher;

    @Override
    public 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;
}

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

复制代码
@Value("${dfs.console.server}")
private String dfsConsoleServer;

其他

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

bash 复制代码
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自动同步k8s的configmap更新_fabric8 更新configmap-CSDN博客

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

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

相关推荐
paopaokaka_luck17 分钟前
基于springboot3+vue3的文山民族文化资源展示与推广平台(协同过滤算法、Echarts 图形化分析)
java·前端·spring boot·学习·echarts
栩栩云生20 分钟前
别再硬记 AWS 和 k8s 命令了!一行命令把十几个云平台的 CLI 全接进 AI
kubernetes·agent·mcp
eralong24 分钟前
Java IO 与 NIO
java·后端
Jesse_EC38 分钟前
我给消息推送加了 Publisher Confirm,然后亲手引入了一个竞态
java
S3rein38 分钟前
JAVA多线程手撕
java·多线程
对象存储与RustFS40 分钟前
给 Kubernetes 找一个对象存储后端:RustFS Helm 部署 + 应用接入实录
后端·云原生·kubernetes
用户81818706274640 分钟前
第7章 死锁排查实录:jstack定位死锁的完整流程
java·后端
喵同志不止步于码农1 小时前
Java Caffeine 快速入门
java·开发语言·后端·并发·缓存一致性
Java成神之路-1 小时前
Java 并发灵魂拷问:notify/signal 唤醒顺序是规范还是实现?
java
Java牛马1 小时前
Caffeine 缓存及其应用相关总结
java·redis·缓存·caffeine·数据一致性·本地缓存