不能在 HTTP handler 里直接跑同步逻辑,因为 I/O 和外部依赖天然阻塞,会导致 goroutine 卡住、超时、资源堆积;应将触发与执行分离,用 channel 传递任务、worker pool 限流、context 控制超时、mutex 防重、显式错误日志。

为什么不能在 HTTP handler 里直接跑同步逻辑
微服务收到配置变更或数据更新请求后,如果在 handler 里直接调用 performBackup() 或 syncToRemote(),会卡住整个 goroutine。HTTP 超时(通常 30s)、内存堆积、连接堆积——不是并发不够,而是 I/O 和外部依赖(如数据库、SFTP、pg_dump)天然阻塞。
真正要做的,是把“触发”和“执行”拆开:handler 只负责发信号(比如往 channel 写个任务 ID),实际备份/同步由独立 goroutine 拿到后异步跑。
- 别用
go syncData()然后不管——没 context 控制、没错误回收、没重试机制,失败就静默丢弃 - 必须用
context.WithTimeout(ctx, 2*time.Hour)包裹执行体,避免 pg_dump 卡死或 SFTP 传输中断导致 goroutine 泄漏 - 启动前检查是否已有同名任务在运行(用 map[string]struct{} + mutex),防止重复触发压垮下游
- 错误必须显式记录:
log.Printf("sync task %s failed: %v", taskID, err),而不是只 return
如何用 channel + worker pool 控制并发同步任务
多个微服务实例或高频配置变更,可能瞬间涌入几十个同步请求。不加限流,SFTP 连接池爆满、数据库连接耗尽、本地磁盘 IO 打满——不是代码写得不好,是资源没被节制。
推荐用带缓冲的 channel 做任务队列,固定数量 worker 消费:
立即学习“go语言免费学习笔记(深入)”;
- 定义信号量:
sem := make(chan struct{}, 5),限制最大并发同步数为 5 - 每个任务启动前先
sem ,完成后 <code><-sem - worker goroutine 从
taskCh <-chan SyncTask读取,处理完再wg.Done() - 别用无缓冲 channel 当队列——任务积压时会阻塞 sender,导致上游 handler hang 住
注意:worker 数量 ≠ sem 容量。worker 是常驻 goroutine,sem 控制的是“同时正在执行”的任务数;两者都设 5 是常见安全值,但大文件同步建议 worker=1、sem=3,防内存溢出。
同步失败后怎么安全续传不丢不重
网络抖动、目标端拒绝写入、SSH 连接断开……同步失败很常见。但随便 retry 会重复写入,跳过又可能漏数据。核心是 checkpoint 必须与目标端持久化强绑定。
- PostgreSQL 增量同步:只有
tx.Commit()成功后,才调用pglogrepl.ReplicationSlotAdvance()更新 LSN;否则下次从旧 LSN 重放 - SFTP 文件同步:上传完成后,必须用
client.Stat(dstPath)确认远程文件 size 和 modtime 匹配,再写本地.checkpoint.json记录该文件已成功 - 本地 JSON 配置备份:写完
os.WriteFile()后,立刻os.Chmod(path, 0600)并校验bytes.Equal(oldHash, newHash),三者全成功才算 checkpoint - 千万别把 checkpoint 写在同步开始前——那是“计划做”,不是“已经做完”
备份文件写本地还是推远程?路径和权限怎么设才不踩坑
开发时写 ./cache/config.bak.json 很方便,但容器部署时,这个路径往往落在只读镜像层里,重启就丢;硬写 /tmp 又可能被系统清理。路径选择本质是部署约束问题。
- 优先读环境变量
XDG_CACHE_HOME,没设则 fallback 到$HOME/.cache/myapp(用户级)或/var/cache/myapp(系统级) - 写之前必须
os.MkdirAll(dir, 0700),否则no such file or directory会让整个备份逻辑静默失效 - 敏感配置文件写完立刻
os.Chmod(path, 0600),防止其他用户或容器内其他进程读取 - 不要用
ioutil.WriteFile(已弃用),统一用os.WriteFile,它原子性更好,且不会意外清空文件权限位
最后提醒一句:所有涉及文件写入的路径,必须用 filepath.Abs() 标准化,尤其当配置来自命令行参数或 env 时,相对路径在不同工作目录下行为不可控。


















