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。

相关推荐
DeeplyMind2 小时前
rocr专栏介绍
linux·ai·amdgpu·rocm·rocr·libhsakmt·thunk
Mr.Aholic12 小时前
Java系列知识之 ~ Spring 与 Spring Boot 常用注解对比说明
java·spring boot·spring
科技峰行者15 小时前
阿里云瓴羊发布企业级AI智能体服务平台AgentOne,成就AI时代的“超级公司”
人工智能·阿里云·ai·云计算·羚羊
多多*19 小时前
2025最新centos7安装mysql8 相关 服务器配置 纯命令行操作 保姆级教程
java·运维·服务器·mysql·spring·adb
CoderJia程序员甲19 小时前
GitHub 热榜项目 - 日榜(2025-09-27)
ai·github·开源项目·github热榜
yk1001020 小时前
Spring属性配置解析机制详解
java·后端·spring
coder-pig21 小时前
Claude Code + Holopix AI | 轻松复刻 “虚假广告“-丧尸射击小游戏
ai·aigc·ai编程·claude code
笨蛋不要掉眼泪21 小时前
SpringBoot项目Excel模板下载功能详解
java·spring boot·后端·spring·excel·ruoyi