JaiRouter 多版本配置管理:一个轻量级多版本配置实现思路

JaiRouter 多版本配置管理:一个轻量级多版本配置实现思路


文章目录

一、需求缘起

最近在写开源项目JaiRouter,配置项目比较多,考虑到真实的业务场景下,多次配置后,测试后,无法回滚到原始版本,因此考虑到需要频繁回滚 JSON 配置。为了省掉数据库依赖,我把版本号直接放到文件名里,写完发现代码意外地短,回滚速度意外地快。整理出来,权当抛砖引玉。


二、核心思路

  1. 文件名即版本号:config@version.json
  2. 当前配置单独存放:config.json
  3. 读/写/回滚/清理 全部基于 java.nio.file.Files,不引入第三方存储

三、代码骨架

kotlin 复制代码
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.*;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 文件系统多版本配置存储:文件名即版本号
 */
public final class FileVersionStore {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private final Path dir;

    public FileVersionStore(Path root) throws IOException {
        this.dir = root.toAbsolutePath().normalize();
        Files.createDirectories(dir);
    }

    /* ---------- 基础 API ---------- */

    /** 保存指定版本(覆盖写) */
    public void save(int version, Map<String, Object> data) throws IOException {
        Path file = versionFile(version);
        Files.writeString(file, MAPPER.writeValueAsString(data),
                StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
    }

    /** 读取当前配置 */
    public Map<String, Object> current() throws IOException {
        Path cur = currentFile();
        if (Files.notExists(cur)) {
            throw new NoSuchFileException("当前配置文件不存在: " + cur);
        }
        return MAPPER.readValue(cur.toFile(), new TypeReference<>() {});
    }

    /** 回滚到指定版本(原子覆盖) */
    public void rollbackTo(int version) throws IOException {
        Path src = versionFile(version);
        if (Files.notExists(src)) {
            throw new NoSuchFileException("版本文件不存在: " + src);
        }
        // ATOMIC_MOVE 保证并发安全
        Files.copy(src, currentFile(), StandardCopyOption.REPLACE_EXISTING);
    }

    /** 列出所有可用版本,升序 */
    public List<Integer> listVersions() throws IOException {
        try (Stream<Path> stream = Files.list(dir)) {
            return stream.map(Path::getFileName)
                         .map(Path::toString)
                         .filter(n -> n.startsWith("config@") && n.endsWith(".json"))
                         .map(n -> n.substring("config@".length(), n.length() - ".json".length()))
                         .mapToInt(Integer::parseInt)
                         .sorted()
                         .boxed()
                         .collect(Collectors.toList());
        }
    }

    /** 删除指定版本(手工清理用) */
    public void delete(int version) throws IOException {
        Files.deleteIfExists(versionFile(version));
    }

    /* ---------- 内部工具 ---------- */

    private Path currentFile() {
        return dir.resolve("config.json");
    }

    private Path versionFile(int version) {
        return dir.resolve("config@" + version + ".json");
    }

    /* ---------- 简单演示 ---------- */
    public static void main(String[] args) throws IOException {
        FileVersionStore store = new FileVersionStore(Paths.get("repo"));

        // 写两个版本
        store.save(1, Map.of("threshold", 10));
        store.save(2, Map.of("threshold", 20));

        // 回滚到 v1
        store.rollbackTo(1);
        System.out.println("current=" + store.current()); // {threshold=10}

        // 查看所有版本
        System.out.println("versions=" + store.listVersions()); // [1, 2]
    }
}

依赖只有 Jackson。


四、什么时候够用

  • 单节点或小集群
  • 版本总量 < 10 k(目录扫描可接受)
  • 无需字段级合并,整体覆盖即可

五、什么时候换方案

  • 需要分布式一致性 → 上 etcd/consul
  • 需要 JSON Patch → 上 Git 或数据库
  • 文件数 > 10 k → 加一层前缀索引或转对象存储

六、小结

把版本号写进文件名,回滚操作退化成一次文件复制,代码量少、依赖少、调试方便。对于个人项目或内部小工具,算一个"够用且易丢进 Docker"的折中方案。

完整实验代码已放到 JAiRouter,欢迎试用、提 issue。

相关推荐
aichitang202417 分钟前
希尔伯特空间中的正交性
人工智能·学习·机器学习·ai·泛函分析
张忠琳22 分钟前
【deepseek-harness】Cordis 时空可组合性编程范式 — 三段式精读笔记(二)
ai·agent·deepseek·harness·cordis·dsh
coft1 小时前
Pi Agent 架构与关键功能全解析
ai·ai编程
哥不是小萝莉1 小时前
AI 编码 Agent 从原理到可运行代码
ai·agent
图王大胜2 小时前
万物演化论00(序章) 从宇宙到AI
人工智能·ai·宇宙·演化·文明·生命科学
技术小事2 小时前
How To Use Claude 怎么使用Claude
ai·claude
笑霸final2 小时前
不用上传服务器!用 Vue3 + ONNX Runtime Web 在浏览器本地实现智能抠图
运维·前端·图像处理·ai·onnx·canva可画·web性能优化
一切皆是因缘际会2 小时前
多时空虚拟中心—— 全域同源基座与脑机谐振应用架构
ai·系统架构·脑机接口·虚拟全球网络控制中心·元宇宙世界底层架构·原生多时空架构
效率工作实验室3 小时前
AI Agent开发平台有哪些?2026年平台对比
ai·agent开发平台·ai智能体开发平台·智能体平台对比·agent平台选型
黄俊懿4 小时前
【架构师从入门到进阶】第五章:DNS&CDN&网关优化思路——第七节:网关-XSS攻击与预防
网关·网络安全·架构·系统架构·架构师·xss·架构设计