FtpTool/docs/ftp-sync-tool-design.md
2026-04-15 15:58:23 +08:00

14 KiB
Raw Blame History

基于 FTP 中转的配置双向同步工具设计方案

1. 文档目的

本文档用于说明一套基于 FTP 中转的配置同步工具设计方案,满足以下目标:

  • 开发环境定时从 Git 拉取新配置,并通过生产环境 push 接口推送到生产
  • 生产环境定时从 pull 接口拉取新配置,并同步回开发环境 Git
  • 在开发环境与生产环境不能直接互通时,通过 FTP 服务作为中转通道完成双向同步

2. 已知约束

2.1 技术约束

  • JDK1.8
  • Spring Boot2.7.18
  • 轻量数据库:H2 或同类开源可商用数据库
  • 其他依赖必须为开源可商用组件

2.2 网络与部署约束

  • 无法登录 FTP 所在服务器主机
  • 只能访问 FTP 服务:IP + 端口 + 用户名/密码
  • 网络拓扑如下:
开发环境 <----> FTP A <----> 生产环境

说明:

  • 开发环境可以访问 FTP A
  • 生产环境可以访问 FTP A
  • 开发与生产不假设可以直接互通

3. 设计原则

  • 同一套程序,按不同 profile 部署在开发和生产两端
  • 通过 FTP 传递标准化同步包,避免环境间直接通信依赖
  • 使用本地状态库记录同步任务、检查点、应答信息,保证可追踪、可恢复
  • 同步流程必须具备幂等控制,避免重复推送、重复提交
  • 开发到生产、生产回开发必须隔离处理,避免双向同步形成死循环

4. 总体方案

推荐采用“双端代理 + FTP 中转 + 本地状态库”架构:

  • Sync-Agent-Dev:部署在开发环境
  • Sync-Agent-Prod:部署在生产环境
  • FTP A:作为唯一中转通道
  • H2:记录同步状态、任务、检查点、重试信息

整体结构如下:

开发环境
  Sync-Agent-Dev
    |- 拉取 Git
    |- 上传/下载 FTP A
    |- 写入 Git

生产环境
  Sync-Agent-Prod
    |- 调用生产 pull 接口
    |- 调用生产 push 接口
    |- 上传/下载 FTP A

中转
  FTP A
    |- dev-to-prod/
    |- prod-to-dev/
    |- ack/
    |- failed/

5. 部署模式

建议只维护一套代码,通过 Spring Profile 控制角色:

  • dev-agent:启用开发侧能力
  • prod-agent:启用生产侧能力

5.1 开发侧职责

  • 定时拉取 Git 指定分支的新配置
  • 判断是否存在新的有效版本
  • 打包配置并上传到 FTP
  • 下载生产侧回传的同步包
  • 将生产侧回传配置写入 Git
  • 提交并推送到远端仓库

5.2 生产侧职责

  • 轮询 FTP获取开发侧上传的配置包
  • 校验后调用生产 push 接口导入配置
  • 定时调用生产 pull 接口拉取最新配置
  • 打包并上传回 FTP供开发侧消费

6. 技术选型

分类 选型 说明
运行时 JDK 1.8 满足约束
框架 Spring Boot 2.7.18 主体框架
调度 Spring Scheduling 实现定时任务
重试 Spring Retry 失败重试
数据库 H2 File Mode 轻量、嵌入式、可持久化
Git 操作 JGit 纯 Java 实现
FTP 操作 Apache Commons Net 主流 FTP 客户端
JSON Jackson 标准序列化组件
日志 SLF4J + Logback 默认日志能力

6.1 数据库模式建议

虽然需求提到“类似 H2 的轻量化内存数据库”,但本场景不建议纯内存模式,原因如下:

  • 服务重启后需要保留同步检查点
  • 失败任务需要支持补偿和人工追踪
  • 需要记录包处理状态,避免重复消费

因此建议使用:

  • H2 File Mode

即本地文件数据库,仍然轻量,但支持状态持久化。

7. 核心业务流程

系统包含两条主链路。

7.1 链路一:开发 Git -> 生产 push 接口

用途:将开发环境 Git 中的新配置推送到生产环境。

流程如下:

  1. dev-agent 定时拉取 Git 指定分支
  2. 判断 Git 最新提交是否为新的有效配置版本
  3. 将配置目录打包为标准同步包
  4. 上传至 FTP 路径 dev-to-prod/out/
  5. prod-agent 轮询 FTP发现新包后下载
  6. 校验包完整性、幂等键和来源信息
  7. 调用生产环境 push 接口导入配置
  8. 成功后生成 ack 文件上传到 FTP
  9. dev-agent 读取 ack,将任务状态更新为成功

建议时序图如下:

sequenceDiagram
    participant G as Git(开发)
    participant D as Sync-Agent-Dev
    participant F as FTP A
    participant P as Sync-Agent-Prod
    participant API as 生产Push接口

    D->>G: 定时 pull 配置
    D->>D: 检查是否有新版本
    D->>D: 打包 zip + manifest
    D->>F: 上传 dev-to-prod/out/
    P->>F: 轮询并下载新包
    P->>P: 校验 hash/traceId
    P->>API: 调用 push 接口
    API-->>P: 返回处理结果
    P->>F: 上传 ack
    D->>F: 读取 ack
    D->>D: 更新状态为成功

7.2 链路二:生产 pull 接口 -> 开发 Git

用途:将生产环境当前配置回传到开发环境,形成配置镜像或审计记录。

流程如下:

  1. prod-agent 定时调用生产 pull 接口
  2. 将返回配置标准化后计算版本标识或内容哈希
  3. 如果与上次同步结果不同,则打包上传到 FTP prod-to-dev/out/
  4. dev-agent 轮询 FTP 并下载新包
  5. 解包后写入本地 Git 工作目录
  6. 提交 commit 并推送到远端 Git
  7. 成功后写回 ack

建议时序图如下:

sequenceDiagram
    participant API as 生产Pull接口
    participant P as Sync-Agent-Prod
    participant F as FTP A
    participant D as Sync-Agent-Dev
    participant G as Git(开发)

    P->>API: 定时调用 pull 接口
    API-->>P: 返回当前配置
    P->>P: 标准化并计算 hash
    P->>F: 上传 prod-to-dev/out/
    D->>F: 轮询并下载新包
    D->>D: 解包并写入工作区
    D->>G: commit + push
    D->>F: 上传 ack

8. 标准同步包设计

为保证跨环境处理一致,建议所有同步内容封装为统一格式的压缩包。

8.1 包结构

package.zip
  |- manifest.json
  |- config/
  |- sha256.txt

8.2 manifest 字段建议

{
  "traceId": "uuid",
  "direction": "DEV_TO_PROD",
  "sourceEnv": "DEV",
  "sourceVersion": "gitCommitId",
  "contentHash": "sha256",
  "createdAt": "2026-04-15T10:00:00+08:00"
}

8.3 字段说明

  • traceId:本次同步唯一流水号
  • direction:同步方向,例如 DEV_TO_PRODPROD_TO_DEV
  • sourceEnv:来源环境
  • sourceVersion:来源版本号,开发侧通常为 Git Commit ID
  • contentHash:配置内容哈希,便于判断重复包
  • createdAt:包生成时间

9. FTP 目录规划

建议在 FTP A 上使用如下目录结构:

/dev-to-prod/out/
/dev-to-prod/ack/
/prod-to-dev/out/
/prod-to-dev/ack/
/failed/

目录说明:

  • /dev-to-prod/out/:开发侧发往生产侧的同步包
  • /dev-to-prod/ack/:生产侧返回的处理应答
  • /prod-to-dev/out/:生产侧发往开发侧的同步包
  • /prod-to-dev/ack/:开发侧返回的处理应答
  • /failed/:失败包归档目录

9.1 上传规范

为避免消费端读取到半截文件,建议采用临时文件上传策略:

  1. 先上传为 .tmp
  2. 上传完成后重命名为正式 .zip
  3. 消费端只处理 .zip 文件

10. Git 分支策略

这是方案中的关键设计点。

不建议将“开发配置推生产”和“生产配置回传开发”写到同一个 Git 分支,否则极易形成循环同步。

建议拆分为两个分支:

  • config-dev-main:开发主配置分支
  • config-prod-snapshot:生产配置镜像分支

同步规则:

  • DEV -> PROD 只消费 config-dev-main
  • PROD -> DEV 只写入 config-prod-snapshot

10.1 这样设计的好处

  • 避免双向同步形成闭环
  • 生产回传配置不会覆盖开发主线
  • 便于审计“生产当前实际配置”

10.2 机器人提交标记

建议同步工具在 commit message 中增加固定前缀,例如:

sync(prod->git): traceId=xxx version=xxx

开发侧扫描 Git 时应忽略同步机器人生成的提交,进一步降低环路风险。

11. 本地状态库设计

建议至少建立以下 3 张表。

11.1 sync_checkpoint

用于记录各方向的最后一次成功检查点。

字段 类型 说明
id bigint 主键
direction varchar 同步方向
last_success_version varchar 最后成功版本
last_success_hash varchar 最后成功内容哈希
updated_at timestamp 更新时间

11.2 sync_task

用于记录每次同步任务生命周期。

字段 类型 说明
id bigint 主键
trace_id varchar 流水号
direction varchar 同步方向
source_version varchar 来源版本
package_name varchar 包文件名
status varchar 状态
retry_count int 重试次数
error_msg clob 错误信息
created_at timestamp 创建时间
updated_at timestamp 更新时间

11.3 sync_ack

用于记录应答信息。

字段 类型 说明
id bigint 主键
trace_id varchar 流水号
ack_side varchar 应答方
ack_status varchar 应答状态
ack_time timestamp 应答时间
remark varchar 备注

12. 幂等与一致性设计

12.1 幂等键建议

建议以如下组合作为幂等键:

direction + sourceVersion + contentHash

约束效果:

  • 已经处理过的包不能重复推送
  • 已经提交过的生产快照不能重复写 Git

12.2 一致性策略

本方案属于跨系统、跨网络的异步同步,不适合做强一致事务。

建议采用:

  • “本地落库 + 外部调用 + 最终一致”模式
  • 每一步记录状态
  • 失败后允许自动重试或人工补偿

13. 失败处理与补偿机制

13.1 自动重试

以下场景建议自动重试:

  • FTP 上传失败
  • FTP 下载失败
  • 生产 push 接口调用失败
  • 生产 pull 接口调用失败
  • Git push 失败

建议策略:

  • 最大重试次数:3 ~ 5
  • 重试间隔:指数退避,例如 30s / 60s / 120s

13.2 失败归档

连续失败后建议:

  • 将包移动到 FTP 的 /failed/
  • 将任务状态置为 FAILED
  • 记录完整错误信息
  • 触发告警

13.3 人工补偿

后续可以增加一个管理接口,支持:

  • traceId 重新执行
  • 重置任务状态
  • 查看失败原因

14. 安全设计

14.1 传输安全

优先级建议如下:

  1. 优先使用 FTPS
  2. 如果只能使用普通 FTP建议对同步包内容做 AES 加密

14.2 凭据管理

以下信息不得写死在代码中:

  • FTP 地址、端口、用户名、密码
  • Git 用户名、密码或 Token
  • 生产接口认证信息

建议通过以下方式外置:

  • application-*.properties
  • 环境变量
  • 启动参数

14.3 审计日志

建议记录:

  • 谁发起了同步
  • 同步方向
  • 来源版本
  • 包名
  • 接口调用结果
  • 异常原因

15. 项目结构建议

有两种实现方式。

15.1 方案 A单工程 + Profile 切换

适用于项目规模较小、交付快的场景。

sync-tool
  |- src/main/java
  |   |- config
  |   |- ftp
  |   |- git
  |   |- job
  |   |- package
  |   |- repository
  |   |- service
  |   |- web
  |- src/main/resources
  |   |- application.properties
  |   |- application-dev-agent.properties
  |   |- application-prod-agent.properties

15.2 方案 B多模块拆分

适用于后续可能演化较多、职责更清晰的场景。

sync-tool
  |- common
  |- dev-agent
  |- prod-agent

当前建议优先采用:

  • 方案 A单工程 + Profile

理由:

  • 实现成本低
  • 运维简单
  • 早期更适合快速打通链路

16. 核心模块划分

建议按职责拆分以下模块:

  • GitService
    • 拉取仓库
    • 检查最新提交
    • 提交并推送生产回传配置
  • FtpService
    • 上传、下载、重命名、目录扫描
  • PackageService
    • 生成 zip
    • 生成 manifest
    • 校验 hash
  • SyncTaskService
    • 任务创建
    • 状态变更
    • 检查点维护
  • ProdPushService
    • 调用生产 push 接口
  • ProdPullService
    • 调用生产 pull 接口
  • AckService
    • 生成和消费 ack 文件
  • JobScheduler
    • 各类定时任务调度

17. 定时任务建议

17.1 开发侧任务

  • GitPullJob
    • 周期拉取 Git 并检查是否有新配置
  • UploadDevPackageJob
    • 将待同步配置上传到 FTP
  • ConsumeProdPackageJob
    • 下载生产回传包并写入 Git
  • AckScanJob
    • 扫描生产侧 ack 并更新任务状态

17.2 生产侧任务

  • ConsumeDevPackageJob
    • 下载开发侧同步包并调用生产 push
  • PullProdConfigJob
    • 定时调用生产 pull 接口
  • UploadProdPackageJob
    • 将拉取结果上传到 FTP
  • AckScanJob
    • 扫描开发侧 ack 并更新任务状态

18. 一期 MVP 建议

建议按最小可交付版本分阶段实施。

阶段 1打通主链路

  • 建立 Spring Boot 工程
  • 集成 H2、JGit、FTP
  • 实现开发到生产的全量包同步
  • 实现生产 push 接口调用

阶段 2打通回传链路

  • 接入生产 pull 接口
  • 实现生产到开发的 FTP 回传
  • 实现开发侧写入 Git 并推送

阶段 3增强稳定性

  • 增加重试
  • 增加 ack 机制
  • 增加失败归档
  • 增加告警与审计日志

19. 风险与注意事项

19.1 最大风险:双向同步闭环

如果生产回传配置写入开发主分支,再被开发侧识别为“新配置”,会再次推送到生产,形成无限循环。

规避措施:

  • 使用独立镜像分支
  • 识别机器人提交
  • 使用幂等键

19.2 配置冲突风险

如果开发和生产都会修改同一份配置,且要求双向合并,则不能简单用文件覆盖方式处理。

当前建议:

  • 将生产回传定义为“镜像/审计”
  • 不直接回写开发主配置分支

19.3 FTP 能力限制

如果 FTP 不支持原子重命名、目录权限受限或稳定性较差,需要额外做兼容与重试。

20. 结论

在当前网络条件下,推荐采用“开发代理 + 生产代理 + FTP 中转 + H2 状态库”的双端部署方案。

该方案具备以下特点:

  • 不依赖开发与生产直接互通
  • 满足开发到生产、生产到开发的双向同步需求
  • 支持状态记录、失败重试、幂等控制和审计追踪
  • 适合使用 Java 1.8 + Spring Boot 2.7.18 快速落地

21. 后续可继续细化内容

后续可以基于本方案继续输出:

  • application.properties 配置项设计
  • H2 建表 SQL
  • 核心类图与接口设计
  • 各定时任务的时序与状态流转
  • Spring Boot 工程骨架