# 基于 Git 直连的配置双向同步工具设计方案 ## 1. 文档目的 本文档用于说明一套基于 Git 直连的配置同步工具设计方案,满足以下目标: - 生产环境定时从开发 Git 拉取新配置,并调用生产 `push` 接口导入生产 - 生产环境定时从生产 `pull` 接口拉取当前配置,并回写到开发 Git - 在 FTP 不再使用的前提下,简化整体架构、降低维护成本 ## 2. 已知约束 ### 2.1 技术约束 - JDK:`1.8` - Spring Boot:`2.7.18` - 轻量数据库:`H2` 或同类开源可商用数据库 - 其他依赖必须为开源可商用组件 ### 2.2 网络与部署约束 - 生产环境可以访问开发 Git 仓库 - 生产环境需要能够调用生产系统 `push/pull` 接口 - FTP 不再使用 建议先确认一个关键前提: - 生产环境是否对开发 Git 具备“读 + 写”权限 说明: - 如果生产环境只能读取 Git,无法推送分支,那么“生产 -> 开发 Git”这条链路不能闭环 - 如果生产环境可以读取和推送 Git,则整套同步可以收敛为单点部署 ## 3. 新架构结论 在新条件下,不再推荐“双端代理 + FTP 中转”。 推荐改为: - **单端代理 + Git 直连 + 本地状态库** 即只在生产环境部署一套同步服务: - `Sync-Agent-Prod` 它同时承担两类任务: 1. 从开发 Git 拉取配置,推送到生产 2. 从生产 `pull` 接口拉取配置,回写到开发 Git 整体结构如下: ```text 开发 Git 仓库 <----> 生产环境 Sync-Agent-Prod <----> 生产系统 push/pull 接口 ``` ## 4. 为什么要改成单端部署 新架构相比旧方案有明显优势: - 去掉 FTP,中转链路减少一跳 - 去掉打包上传、轮询下载、ACK 回执等中间环节 - 部署节点减少为 1 个,运维更简单 - 故障点减少,排查路径更短 - 数据流更直接,状态一致性更容易控制 ## 5. 总体方案 推荐在生产环境部署唯一同步实例: - `Sync-Agent-Prod` 其职责如下: - 拉取开发 Git 主配置分支 - 检查是否存在待下发的新版本 - 调用生产 `push` 接口导入配置 - 定时调用生产 `pull` 接口获取当前生产配置 - 将生产配置写回 Git 快照分支 - 使用 H2 记录同步状态、检查点、失败记录 ## 6. 技术选型 | 分类 | 选型 | 说明 | | --- | --- | --- | | 运行时 | JDK 1.8 | 满足约束 | | 框架 | Spring Boot 2.7.18 | 主体框架 | | 调度 | Spring Scheduling | 实现定时任务 | | 重试 | Spring Retry | 失败重试 | | 数据库 | H2 File Mode | 持久化检查点与任务状态 | | Git 操作 | JGit | 生产环境直接读写 Git | | HTTP 调用 | RestTemplate | 调用生产 `push/pull` 接口 | | JSON | Jackson | 标准序列化 | | 日志 | SLF4J + Logback | 默认日志能力 | 说明: - FTP 客户端依赖在新方案里已经不是核心能力 - 标准同步包、FTP 目录、ACK 文件等设计可以整体下线 ## 7. 部署模式 ### 7.1 推荐模式 推荐只部署: - `prod-agent` 不再需要: - `dev-agent` - FTP 中转服务 ### 7.2 运行位置 同步工具建议运行在生产环境可控节点上,要求: - 能访问开发 Git - 能访问生产 `push/pull` 接口 - 能持久化本地 H2 文件数据库 ## 8. 两条核心链路 ### 8.1 链路一:开发 Git -> 生产 push 接口 用途: - 将开发配置分支中的新配置同步到生产环境 流程如下: 1. `Sync-Agent-Prod` 定时拉取开发 Git 指定分支 2. 获取最新提交版本号,例如 Git Commit ID 3. 判断该版本是否已成功同步 4. 如果未同步,则导出配置目录 5. 调用生产 `push` 接口导入配置 6. 成功后更新本地检查点和任务状态 建议时序图如下: ```mermaid sequenceDiagram participant G as Git(开发) participant P as Sync-Agent-Prod participant API as 生产Push接口 P->>G: 定时 pull config-dev-main P->>P: 判断是否有新 commit P->>P: 导出配置目录 P->>API: 调用 push 接口 API-->>P: 返回处理结果 P->>P: 更新 sync_task / checkpoint ``` ### 8.2 链路二:生产 pull 接口 -> 开发 Git 用途: - 将当前生产配置快照回写到开发 Git,用于镜像、审计、回溯 流程如下: 1. `Sync-Agent-Prod` 定时调用生产 `pull` 接口 2. 将返回结果标准化并计算内容哈希 3. 判断该版本或哈希是否已同步 4. 如果未同步,则切换到生产快照分支 5. 写入配置文件 6. 提交 commit 并 push 到开发 Git 7. 成功后更新本地检查点和任务状态 建议时序图如下: ```mermaid sequenceDiagram participant API as 生产Pull接口 participant P as Sync-Agent-Prod participant G as Git(开发) P->>API: 定时调用 pull 接口 API-->>P: 返回当前生产配置 P->>P: 标准化并计算 hash/version P->>G: checkout config-prod-snapshot P->>G: commit + push P->>P: 更新 sync_task / checkpoint ``` ## 9. Git 分支策略 这个设计点仍然必须保留。 不建议将“开发配置推生产”和“生产配置回写 Git”使用同一个 Git 分支,否则非常容易形成同步闭环。 推荐分支如下: - `config-dev-main`:开发主配置分支 - `config-prod-snapshot`:生产配置镜像分支 同步规则: - `Git -> PROD` 只消费 `config-dev-main` - `PROD -> Git` 只写入 `config-prod-snapshot` ### 9.1 这样设计的价值 - 避免生产回写内容再次触发下发 - 生产快照不会污染开发主线 - 便于审计“生产当前实际配置” ### 9.2 机器人提交标记 建议同步工具统一使用固定 commit message 前缀,例如: ```text sync(prod->git): traceId=xxx version=xxx ``` 同时: - `Git -> PROD` 扫描时只关注 `config-dev-main` - 不读取 `config-prod-snapshot` ## 10. 状态库设计 新方案建议保留以下核心表: ### 10.1 `sync_checkpoint` 用于记录各方向最后一次成功同步的检查点。 关键字段: - `direction` - `last_success_version` - `last_success_hash` - `updated_at` ### 10.2 `sync_task` 用于记录每次同步任务生命周期。 关键字段: - `trace_id` - `direction` - `source_version` - `content_hash` - `status` - `retry_count` - `error_msg` ### 10.3 `sync_ack` 在新架构下: - 不再作为跨节点 ACK 使用 - 已退出当前主 schema 如果后续需要审计扩展,可以单独恢复为接口调用日志表。 ## 11. 幂等设计 建议继续使用以下组合作为幂等键: ```text direction + sourceVersion + contentHash ``` 作用: - 同一开发版本不会重复推生产 - 同一生产快照不会重复写 Git ## 12. 失败处理与补偿 ### 12.1 自动重试 以下场景建议自动重试: - Git pull 失败 - Git push 失败 - 生产 `push` 接口调用失败 - 生产 `pull` 接口调用失败 建议策略: - 最大重试次数:`3 ~ 5` - 指数退避:`30s / 60s / 120s` ### 12.2 失败落库 失败后建议: - 更新 `sync_task.status=FAILED` - 记录异常堆栈摘要 - 增加重试次数 - 保留最近一次成功检查点不变 ### 12.3 人工补偿 后续可增加管理接口,支持: - 按 `traceId` 重试 - 按方向重跑最近一次失败任务 - 查询最近同步记录 ## 13. 安全设计 ### 13.1 Git 访问建议 推荐使用: - HTTPS + Token 或: - SSH Deploy Key ### 13.2 权限建议 生产环境访问 Git 的账号建议采用最小权限原则: - 对 `config-dev-main` 至少有读取权限 - 对 `config-prod-snapshot` 需要推送权限 更理想的做法: - 使用专用机器人账号 - 对开发主分支启用保护 - 限制机器人只写快照分支 ### 13.3 生产接口认证 生产 `push/pull` 接口建议使用: - `Bearer Token` - HTTPS ## 14. 项目结构建议 新架构下建议进一步简化模块职责: ```text sync-tool |- src/main/java | |- config | |- git | |- job | |- repository | |- service | |- web |- src/main/resources | |- application.properties | |- application-prod-agent.properties ``` 说明: - `prod-agent` 是唯一正式运行角色 - `dev-agent` 与 FTP 相关模块已退出主运行面 ## 15. 核心模块划分 建议保留并聚焦以下模块: - `GitClientService` - clone / pull / checkout / commit / push - `ProdConfigApiService` - 调用生产 `push/pull` 接口 - `SyncTaskService` - 任务创建、状态变更、重试次数维护 - `CheckpointService` - 成功检查点维护 - `ProdSyncCoordinator` - 串联双向同步流程 - `JobScheduler` - 定时调度 已退出主运行面: - FTP 包上传下载逻辑 - FTP ACK 逻辑 - 双端代理运行路径 ## 16. 定时任务建议 新架构下推荐保留两类核心任务: ### 16.1 `GitToProdSyncJob` 职责: - 拉取 `config-dev-main` - 判断是否有新 commit - 调用生产 `push` 接口 ### 16.2 `ProdToGitSnapshotJob` 职责: - 调用生产 `pull` 接口 - 判断是否有新快照 - 提交到 `config-prod-snapshot` 可选任务: - `RetryFailedTaskJob` - `HealthCheckJob` ## 17. 一期 MVP 建议 建议重新按最小可交付版本收敛: ### 阶段 1:打通 Git -> 生产 - 生产环境直连开发 Git - 实现 `config-dev-main` 拉取 - 实现生产 `push` 接口调用 - 落库记录同步状态 ### 阶段 2:打通 生产 -> Git - 接入生产 `pull` 接口 - 回写 `config-prod-snapshot` - 实现 commit + push ### 阶段 3:增强稳定性 - 补充重试 - 补充管理接口 - 补充告警与审计日志 ## 18. 风险与注意事项 ### 18.1 最大风险:Git 写权限不足 如果生产环境对开发 Git 没有推送权限,则“生产 -> Git”链路无法完成。 解决方案: - 申请机器人账号 - 或将“生产回写 Git”改成调用开发侧服务接口 ### 18.2 最大风险:双向同步闭环 如果生产回写到了开发主分支,会再次触发下发。 规避措施: - 使用独立快照分支 - 不扫描快照分支 - 使用幂等键和机器人提交标记 ### 18.3 最大风险:生产直连开发 Git 的安全边界 需要明确: - 网络访问是否合规 - Git 账号权限是否受控 - Token 或 SSH Key 是否可轮换 ## 19. 结论 在“生产环境可以直接访问开发 Git,FTP 不再需要”的前提下,推荐将旧方案调整为: - **生产环境单点部署** - **Git 直连** - **保留生产 `push/pull` 接口** - **保留 H2 状态库** 这是比原来 FTP 中转更合适的方案,原因是: - 架构更简单 - 故障点更少 - 链路更短 - 运维成本更低 ## 20. 下一步建议 下一步建议按下面顺序推进: 1. 先确认生产环境对开发 Git 是否具备推送权限 2. 确认生产 `push/pull` 接口最终协议 3. 删除退役标记文件 `application-dev-agent.properties` 4. 将工程命名中残留的 `ftp` 语义继续清理 5. 补充新的 `application-prod-agent.properties` 配置说明