SpringCloudConfigServer配置刷新优化方案

news2024/12/23 9:28:30

前一文章《SpringCloudConfigServer配置中心使用与刷新详解》 介绍了Spring Cloud原生配置中心的部署方案,以及配置变更时的刷新方案。
通过该文可以看到:

  • 第一种方案无法同时刷新单个服务的所有实例
  • 第二种方案依赖于消息中间件(RabbitMQ或kafka)比较重,我测试中还碰到客户端反序列化异常导致故障的情况

基于如上2个原因,希望有一个比较轻量的配置刷新方案,思考了一下,是否可以由Client定时轮询,如果配置有更新,就自己调用自己的/actuator/refresh接口,完成配置刷新呢?
验证了一下可行性,是没有问题的:

  • Config-Server支持添加@RestController接口
  • Client调用自己的/actuator/refresh接口,就是http发请求给自己,调用localhost:端口就可以
    根据/actuator/refresh接口调用日志,在对应的类上打断点,跟踪了一下,发现该接口定义在org.springframework.cloud.endpoint.RefreshEndpoint类里,调用的是org.springframework.cloud.context.refresh.ConfigDataContextRefresher类的refresh()方法,并且该类也注册为Bean了;
    那就更简单了,直接定义一个Job,调用这个Bean的refresh()就好了

自定义方案原理介绍

自定义实现的轮询机制,大致步骤:

  • Config-Server端:
    • 提供管理API,用于开发人员更改 最近配置刷新时间
    • 提供客户端API,用于客户端定时拉取最近的配置刷新时间,并判断是否需要重新加载配置和刷新
  • Config-Client端:
    • 定时轮询Config-Server端的API,获取自己的最近的配置刷新时间;
    • 如果比上一次刷新时间大,则进行配置刷新

整体的刷新流程图如下:
在这里插入图片描述

代码实现

Config-Server端

1、定义全局时间和每个应用的时间,并提供读写方法,核心代码如下:

@Service
public class RefreshService implements InitializingBean {
    private LocalDateTime globalLastUpdateTime = LocalDateTime.now();

    private Map<String, LocalDateTime> mapAppLastUpdateTimes = new HashMap<>();

    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-DD HH:mm:ss.SSS");

    /**
     * 客户端使用:获取指定app的配置更新时间
     */
    public String getLastUpdateTime(String appName) {
        appName = formatAppName(appName);

        LocalDateTime time = null;
        if (StringUtils.hasLength(appName)) {
            time = mapAppLastUpdateTimes.get(appName);
        }
        if (time == null) {
            time = globalLastUpdateTime;
        }
        return time.format(formatter);
    }

    /**
     * 管理端使用:设置指定app的配置更新时间,以触发该app更新配置
     */
    public void setLastUpdateTime(String appName, LocalDateTime time) {
        appName = formatAppName(appName);

        if (!StringUtils.hasLength(appName))
            throw new RuntimeException("应用名不能为空");
        if (time == null)
            time = LocalDateTime.now();

        mapAppLastUpdateTimes.put(appName, time);
        log.info("{}最后配置更新时间修改为:{}", appName, time);
    }

    /**
     * 管理端使用:设置全局配置更新时间,触发所有app的配置更新
     */
    public void setGlobalLastUpdateTime(LocalDateTime time) {
        if (time == null)
            time = LocalDateTime.now();
        globalLastUpdateTime = time;
        mapAppLastUpdateTimes.clear();
        log.info("全局最后配置更新时间修改为:{}", time);
    }

    private String formatAppName(String appName) {
        if (appName == null)
            return "";
        return appName.trim().toLowerCase();
    }

2、提供对外API

@RestController
public class DefaultController {
    private final RefreshService refreshService;

    public DefaultController(RefreshService refreshService) {
        this.refreshService = refreshService;
    }

    /**
     * 查看指定应用的的最近配置更新时间
     *
     * @param appName 应用
     * @return 时间
     */
    @GetMapping("lastTime")
    public String getTime(@RequestParam(required = false) String appName) {
        return refreshService.getLastUpdateTime(appName);
    }

    /**
     * 修改指定应用的最近配置更新时间为now
     *
     * @param appName 应用
     * @return 更新后时间
     */
    @PostMapping("lastTime")
    public String setTime(@RequestParam(required = false) String appName) {
        refreshService.setLastUpdateTime(appName, LocalDateTime.now());
        return getTime(appName);
    }

    /**
     * 修改所有应用的最近配置更新时间为now
     *
     * @return 更新后时间
     */
    @PostMapping("lastAllTime")
    public String setGlobalTime() {
        refreshService.setGlobalLastUpdateTime(LocalDateTime.now());
        return getTime(null);
    }
}

Config-Client端

1、定义feign类以获取配置更新时间

// url就是上面的Config-Server的url地址,可以写入yml配置
@FeignClient(name = "config-server", url = "http://localhost:8999")
public interface FeignConfigServer {
    @GetMapping("/lastTime")
    String getTime(@RequestParam String appName);
}

2、定时job轮询Config-Server端API,并判断是否刷新配置

@Component
@ConditionalOnProperty(value = "spring.cloud.config.refresh.enabled", havingValue = "true", matchIfMissing = true)
@RequiredArgsConstructor
@Slf4j
public class ConfigsRefresh implements InitializingBean {

    private final FeignConfigServer feignConfigServer;
    private final org.springframework.cloud.context.refresh.ConfigDataContextRefresher refresher;

    @Value("${spring.application.name:}")
    private String appName;

    private LocalDateTime lastUpdateTime = LocalDateTime.now();
    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-DD HH:mm:ss.SSS");
    private static final Random RANDOM = new Random();

    /**
     * 每分钟检查并刷新
     */
    @Scheduled(cron = "0 * * * * *")
    public void doRefresh() {
        LocalDateTime serverUpdateTime = getServerLastUpdateTime(appName);
        if (serverUpdateTime.compareTo(lastUpdateTime) <= 0) {
            return;
        }
        log.info("本地更新时间较小:{} < {} 需要刷新配置", lastUpdateTime, serverUpdateTime);
        sleep();

        Set<String> keys = refresher.refresh();

        lastUpdateTime = serverUpdateTime;
        log.info("更新列表 {}", keys);
    }

    // 获取当前应用,配置中心的配置最近刷新时间
    private LocalDateTime getServerLastUpdateTime(String appName) {
        try {
            String serverUpdateTime = feignConfigServer.getTime(appName);
            // log.info("配置中心最近更新: {}", serverUpdateTime);
            if (StringUtils.hasLength(serverUpdateTime)) {
                return LocalDateTime.parse(serverUpdateTime, formatter);
            }
        } catch (Exception exp) {
            log.error("取配置中心更新时间出错:", exp);
        }
        return LocalDateTime.MIN;
    }

    private void sleep() {
        // 随机休眠,避免所有pod同时刷新配置
        int selectSecond = RANDOM.nextInt(10);
        try {
            Thread.sleep(selectSecond * 1000L);
        } catch (InterruptedException e) {
            log.error("休眠出错", e);
        }
    }

    @Override
    public void afterPropertiesSet() {
        lastUpdateTime = LocalDateTime.now();
        log.info("配置本地更新时间:{}", lastUpdateTime);
    }
}

Demo代码参考:

Config-Server端Demo | Config-Client端Demo

实际工作中使用步骤简述

假设Config-Server端url为 http://localhost:8999

单个应用刷新

假设需要刷新配置的应用,spring.application.name=cb-admin
1、修改配置,并提交到git(注意提交到正确的分支)
2、调用配置中心API,通知刷新,调用方式:
curl -X POST http://localhost:8999/lastTime?appName=cb-admin
3、OK,1分钟左右,该应用就会完成配置刷新

所有应用刷新

如果希望所有应用同时刷新,需要调用另一个API:
curl -X POST http://localhost:8999/lastAllTime

查看配置最近更新时间

浏览器里访问:
http://localhost:8999/lastAllTime

未解决的问题

在测试过程中,Config-Server使用了https,结果发现/actuator/refresh无法刷新配置,因为启动能正常加载配置,只是无法刷新,搞了1,2天,后面新建了一个空项目才发现是刷新时报ssl证书无法验证的问题。
可是启动时也是同样的配置加载过程,就比较奇怪。
在跟踪过程中,发现Config-Client使用的是自己内部new的RestTemplate,而不是Spring里的Bean,所以也无法通过重定义RestTemplate的Bean方式来忽略ssl证书校验,
我知道在Config-Server里,可以添加配置spring.cloud.config.server.git.skip-ssl-validation: true来忽略git的ssl证书认证,但是没找到client的类似配置,这个问题就先忽略了,没有进一步去跟踪代码。
后面有时间再研究吧,先用http方式处理了,内部调用也节约一些性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/582206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动驾驶汽车的安全技术特点

“安全第一”是自动驾驶的核心理念和价值观。 自动驾驶车辆的整体系统安全设计是一项复杂的系统工程&#xff0c; 涉及车载自动驾驶系统的核心算法策略设计、 硬件和软件冗余安全设计、远程云代驾技术、 全流程测试验证技术等&#xff0c; 并遵循功能安全&#xff08;ISO 2626…

《数据库应用系统实践》------ 酒店客房管理系统

系列文章 《数据库应用系统实践》------ 酒店客房管理系统 文章目录 系列文章一、需求分析1、系统背景2、 系统功能结构&#xff08;需包含功能结构框图和模块说明&#xff09;3&#xff0e;系统功能简介 二、概念模型设计1&#xff0e;基本要素&#xff08;符号介绍说明&…

品种小组2期—凯利公式在RFI策略中的运用

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 大家好&#xff0c;今天我们来聊一聊松鼠2期V2版本的阶段内容——凯利公式在RFI择时框架上的运用。 松鼠品种小组2期第1版策略、讲解视频已完结&#xff0c;该期小组我们分享了全新“普适性、自适应”择…

0基础学习VR全景平台篇第32章:场景功能-嵌入视频

大家好&#xff0c;欢迎观看蛙色VR官方系列——后台使用课程&#xff01; 一、本功能将用在哪里&#xff1f; 嵌入功能可对VR全景作品嵌入【图片】【视频】【文字】【标尺】四种不同类型内容&#xff1b; 本次主要带来视频类型的介绍&#xff0c;通过嵌入视频功能&#xff0c;…

python+django+vue关爱无主狗流浪狗动物领养公益网站

很多的家庭都开始有养个小宠物的习惯&#xff0c;平时可以排解寂寥。随着近些年来大家养宠物的数量逐日剧增,人类对自己行为的规范与责任感渐渐的缺失,从而造成社会上的流浪动物也越来越多。流浪狗的伤人的事件则更多的出现在了报纸&#xff0c;电视&#xff0c;网络上。国家社…

git 项目演练:007

接下来进行项目演练&#xff0c;这是一个项目提交到git一个完整过程 1. 创建一个项目&#xff0c; 如下&#xff0c;我创建了一个“测试项目” 2. 将项目添加到git管理仓库&#xff0c;打开Git Bash&#xff0c; cd到“测试项目”中 3. 使用git init 命令将项目添加&#xff0c…

python基于pygame库实现弹幕效果(多行显示,速度不同,颜色不同,循环显示)

一、实现目标 使用python实现类似弹幕的显示效果,弹幕文本存储在txt文件中,弹幕多行显示,弹幕颜色不同有区别,弹幕的速度不一,弹幕要循环显示。 弹幕文本数据:danmu.txt 永远的神 哥哥,好帅啊 啊啊啊啊 太好看啦 666 爱了爱了 啦啦啦啦啦啦 牛逼!牛逼!牛逼! 厉害啊…

5G的遮羞布又被撕了,只是这次是韩国,5G为何变成如此模样?

日前韩国公平交易委员会发布声明&#xff0c;对韩国三大运营商重罚336亿韩元&#xff0c;原因是韩国夸大5G网速&#xff0c;实际网速不到理论速率的十分之一&#xff0c;可以说韩国撕下了5G的遮羞布。 韩国公平交易委员会指出运营商宣传5G的时候强调5G的理论速度可以达到10Gbps…

JavaScrip练习

HTMLJS计算器 <!DOCTYPE html> <html> <head><title>Calculator</title> </head> <body> <!-- 计算器显示 --> <input type"text" id"display" disabled> <br><!--计算器按钮onclick &am…

数据偏度介绍和处理方法

偏度&#xff08;skewness&#xff09;是用来衡量概率分布或数据集中不对称程度的统计量。它描述了数据分布的尾部&#xff08;tail&#xff09;在平均值的哪一侧更重或更长。偏度可以帮助我们了解数据的偏斜性质&#xff0c;即数据相对于平均值的分布情况。 有时&#xff0c;正…

自主品牌份额持续提升!福特CEO表态,中国车企才是竞争对手

2023年1-4月&#xff0c;中国市场自主品牌乘用车新车交付275.13万辆&#xff0c;份额占比48.63%&#xff0c;两项数字分别比上年同期增长4.68%&#xff0c;以及提升约3个百分点。其中&#xff0c;新能源汽车继续成为主要推动因素。 “中国电动汽车制造商是我们的主要竞争对手&a…

使用Vuex进行状态管理

在Vue.js应用程序中&#xff0c;状态管理是一个重要的主题。当应用程序变得复杂&#xff0c;组件之间的状态共享和通信变得困难&#xff0c;这时候使用Vuex就会变得十分有用。Vuex是一个专门为Vue.js设计的状态管理库&#xff0c;它提供了一个集中式的状态管理方案&#xff0c;…

Logisim 头歌 偶校验编码设计图解及代码(计算机组成原理)

努力是为了不平庸~ 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。 急的同学请直接点击目录跳到下方解答处&#xff01;&#xff01; 目录 图解&#xff1a; 代码题解&#xff08;免费&#xff09;&#xff1a; 实…

PyTorch-Sequential

Cifar-10用的模型结构&#xff1a; 第一次卷积 (3,32,32) to (32,32,32) 卷积核&#xff08;55&#xff09;&#xff0c;我们需要进行一下计算&#xff0c;看看padding和stride是多少: 未引入Sequential前&#xff1a; import torch from torch import nn from torch.nn impo…

如何用chatGPT赚钱?

赚钱思路 1&#xff09;初级-账号 对于新事物的出现&#xff0c;很多人对此都是抱着一个看热闹的态度&#xff0c;大家对于这个东西的整体认知水平是很低的&#xff01; 所以这个时候的思路就是快速去抢占市场&#xff0c;去各个平台发一些和ChatGPT相关的视频和文章去抢占市…

速递:惠州学院生科院副院长谢海伟一行莅临易基因科技参观交流 | 校企合作

2023年05月26日上午&#xff0c;惠州学院生命科学学院副院长谢海伟教授、陈兆贵教授、陈勇智博士、龚浩博士及生科院本科毕业生陈佳良一行5人莅临深圳市易基因科技有限公司参观交流&#xff0c;并举行“惠州学院实践教学基地“挂牌仪式。易基因总经理王君文、副总经理丁振东等公…

vue项目中使用depcheck检查缺失的依赖项目

使用depcheck检查缺失的项目依赖 由来&#xff1a;今天在做地铁的时候&#xff0c;刷短视频发现一个非常好用的东西&#xff0c;分享一下 它可以帮助我们找出问题&#xff0c;在 package.json 中&#xff0c;每个依赖包如何被使用、哪些依赖包没有用处、哪些依赖包缺失。它是解…

EMC测试中放大器的线性度验证

在进行EMC抗扰度测试时&#xff0c;必须在所有适用的测试频率下检查所用射频功率放大器的线性度。检查方法如下&#xff1a; 确定产生测试电平所需的功率&#xff0c;包括调制。例如: 测试电平:10v /m 调制:80% AM, 1 kHz 所需场强包括调制:18 V/m 辐射抗干扰校准配置示例 在特…

【LED子系统】九、数据结构详解(番外篇)

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强公司&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录…

AI大爆炸 | ChatGPT引爆AI热潮,大佬怎么看?

ChatGPT在AI领域点起的大火&#xff0c;从去年烧到了今年。以ChatGPT为代表的AIGC(人工智能生成内容)在全球风靡&#xff0c;多个代表性AI大模型纷纷涌现&#xff0c;不少业内人士高呼以人机智能融合为主要特征的“AI 2.0时代”已经来临。      有人为人工智能引领未来的广…