用SVN仓库镜像同步工具svnsync,或者通过服务端hooks钩子脚本双写提交,这是实现一次提交同时到达两个服务器的两种主流方案,其中svnsync操作更安全、配置更简单,适合大多数团队。
为什么需要一次提交到两个服务器
先讲一个真实场景,某个开发团队在本地机房维护着SVN服务器,随着业务增长,他们在异地又搭建了一台备份服务器,刚开始是每天凌晨用脚本打包拷贝,但有一次磁盘故障,当天的代码记录全部丢失,几个人加班到凌晨才恢复,从那以后,团队开始寻找实时同步方案。
类似的需求往往出现在三种情境里:
- 异地容灾备份,主服务器宕机时能快速切换
- 测试环境和生产环境需要保持代码一致
- 团队分布在多个城市,各自就近访问不同的SVN服务器
在动手配置之前,先要分清两个概念,很多人在搜索引擎里输入“svn怎么一次提交两个服务器”,实际上问的是两类不同的事。
一是工作副本同时提交到两个版本库。 比如本地MD5值校验,SVN客户端本身不支持直接配置多个服务器地址同时提交。
二是让两个SVN服务器之间保持同步。 你只需要正常提交到一个服务器,它通过机制自动复制到另一个服务器,用户感知上就是一次提交完成了两台服务器的更新,这才是多数人真正要的方案。
行业共识认为,第二种方式才是标准做法,第一种方式没有任何官方支持,即便用脚本硬凑也容易造成版本库损坏。
svn怎么一次提交两个服务器:核心原理先说清楚
SVN本身是一个中心化版本控制系统,一个工作副本对应一个仓库地址,所谓的“一次提交两个服务器”,本质是利用服务端能力实现仓库级的数据同步,客户端无需任何改动。
目前主流的同步方式有两种,各有适合的场景。
svnsync仓库镜像。 这是SVN官方提供的同步工具,它会把一个仓库的提交记录原封不动地复制到另一个仓库,目标仓库变成源仓库的镜像,我们只需要定期触发同步,或者让同步在每次提交后自动运行。
hooks钩子脚本。 在SVN服务器的hooks目录放一个post-commit脚本,当有代码提交到主仓库时,脚本自动把这次提交的版本号传给另一个仓库,在那边执行更新操作。
两者对比差异明显,svnsync更稳定,因为它是官方工具,底层直接解析版本库数据,不依赖工作副本环境,hooks脚本更灵活,可以在提交后同时做很多事,比如触发CI构建、发通知邮件,但脚本一旦写错,可能阻塞主服务器的提交操作。
svnsync配置步骤(推荐)
下面以Linux服务器为例,逐步演示如何搭建svnsync镜像同步,假设两台服务器的信息为:
- 主服务器:svn://192.168.1.10/repos/project
- 镜像服务器:svn://192.168.1.20/repos/project
第一步:创建镜像仓库
在镜像服务器上先创建一个空仓库,注意不能用svnadmin create之后直接往里传东西,它必须是一个干净的、还没有任何提交的仓库。
svnadmin create /var/svn/repos/project
第二步:修改镜像仓库的hook
SVN的机制要求,镜像仓库不能直接写入数据,只有通过svnsync同步的提交才被允许,所以要在镜像仓库的hooks目录创建pre-revprop-change脚本。
cd /var/svn/repos/project/hooks cp pre-revprop-change.tmpl pre-revprop-change chmod +x pre-revprop-change
默认是空的,但必须存在且可执行,否则后续初始化镜像时会被拒绝,这一步是配置svnsync最常见的坑。
第三步:初始化镜像
在镜像服务器上执行svnsync init命令,告诉它源仓库在哪里,目标仓库在哪里。
svnsync init file:///var/svn/repos/project svn://192.168.1.10/repos/project
这里需要注意,第一个参数是镜像仓库的本地路径,第二个参数是主仓库的访问地址,初始化成功后,SVN会把主仓库的版本号记录与镜像仓库关联起来。
第四步:执行首次同步
svnsync sync file:///var/svn/repos/project
首次同步会把主仓库的所有历史提交全部复制到镜像仓库,如果主仓库的提交记录比较多,这一步会耗时较久,事后可以通过svn log命令在镜像仓库里验证历史记录是否完整。
第五步:设置自动同步
每次手动执行sync命令并不现实,配置一个定时任务或者提交后自动触发更实用,推荐两种方式配合使用。
定时同步适合做兜底,在crontab里添加:
/5 /usr/bin/svnsync sync file:///var/svn/repos/project
实时同步则依赖主仓库的post-commit钩子,在主仓库hooks目录新建post-commit文件:
#!/bin/sh REPOS="$1" REV="$2" ssh user@192.168.1.20 "svnsync sync --non-interactive file:///var/svn/repos/project"
这个脚本每次提交后都会通过SSH触发镜像服务器执行一次同步,延迟通常控制在秒级。
hooks脚本双提交配置
如果你对svnsync的定时机制不够放心,或者需要更精细的控制,可以在主服务器的post-commit钩子脚本里直接操作第二个仓库,这种做法的核心思路是:提交发生在主仓库后,脚本用svn commit命令把同样的变更提交到目标仓库。
先来看一个标准的post-commit脚本长什么样:
#!/bin/sh REPOS="$1" REV="$2" /usr/bin/svn update /path/to/working_copy cd /path/to/working_copy /usr/bin/svn commit -m "Sync from r$REV" --username sync --password
这个脚本有一个致命的缺陷:它假设工作副本永远保持干净,没有本地未提交的修改,如果开发人员忘了update就开始改代码,脚本执行时会把他们的本地改动一起提交上去,这会造成数据污染,所以这种方式在多人协同时几乎不可用。
还有一种变体是用svn export导出指定版本的文件,然后强制覆盖另一个仓库的工作副本,再执行add和commit操作,它避免了本地修改冲突,但是效率低下,每次提交都要全量导出一次文件。
业内专家指出,hooks双提交更适合部署在自动化脚本环境里,比如服务器之间同步配置文件、发布静态资源产物等,不太适合直接介入开发流程的主干版本管理。
svnsync与hooks双提交对比
| 对比维度 | svnsync | hooks脚本双提交 |
|---|---|---|
| 数据安全性 | 仓库级复制,不丢历史记录 | 依赖工作副本状态,可能带入脏数据 |
| 配置难度 | 一次性配置,后续低维护 | 需要反复调试脚本,维护成本高 |
| 实时性 | 秒级延迟,取决于触发方式 | 提交后立即执行,但脚本运行时间占用提交通道 |
| 适用场景 | 异地容灾、持续备份 | 特定目录发布、构建部署 |
| 风险点 | 镜像仓库不可直接写 | 脚本阻塞SVN服务器提交 |
两者不是互斥的,在一个生产环境里,完全可以用svnsync做实时镜像,再用hooks触发自动化构建,很多团队实际就是这样组合使用的。
配了双服务器同步之后的常见故障排查
同步配置不是一劳永逸的,瑞典一家开发工具公司在技术博客里提到,大部分svnsync故障出现在权限与网络波动上,以下是几个高频问题。
svnsync初始化时报错“pre-revprop-change hook failed”
这个提示几乎可以断定是镜像仓库的pre-revprop-change脚本缺失或者没有执行权限,检查步骤为:
ls -l /var/svn/repos/project/hooks/pre-revprop-change
如果文件不存在,从模板复制一份,如果存在但没有x权限,执行chmod +x。
同步到一半报错“Repository has not been properly initialized”
这通常是因为svnadmin create之后,有人用svn import往镜像仓库导入过数据,svnsync要求镜像仓库保持完全空白,重新建仓即可。
post-commit钩子脚本执行很久不结束
把SVN命令写进post-commit脚本时,要注意钩子脚本阻塞提交,如果脚本里执行了svn update或svn commit,它会等待版本库锁释放,而版本库锁正被当前提交占着,直接形成死锁,避免的方法是在脚本后台运行:
nohup /usr/bin/svnsync sync --non-interactive file:///var/svn/repos/project &
两台服务器网络不稳定导致同步失败
svnsync支持断点续传,同步失败后不会损坏数据,只要网络恢复重新执行sync命令即可,为了避免频繁失败,可以在脚本里加上循环重试机制,同步失败后等待几秒再重新尝试,连续尝试5次仍然失败就发送告警邮件。
为什么你搜不到svn dual commit的现成方案
有些开发者在配置过程中发现,SVN的官方文档里没有直接提供“一次提交两个服务器”的开关,对比Git,它天然支持给一个仓库配置多个remote地址,push时一键推送多个远端,而SVN没有对应功能,svn dual commit”这类关键词搜出来的内容很少。
近期在一些技术社区里,有用户询问svn一次提交两个服务器的价格,实际上这不算一个商业功能,不存在收费或免费的区别,它完全依靠服务器端配置实现,成本由你自己的时间和服务器资源决定,如果你看到有人售卖相关脚本或服务,你需要核实对方是否可维护,因为一旦脚本出了问题,你很难自己排查。
如果需要更简单的容灾策略,可以直接把两台服务器中的一台作为纯读备份,用mysql主从的思路理解,svnsync的从库本身就不接受直接写入。
如果你正在纠结svn服务器切换配置哪个方便,可以留意:svnsync模式下,切换只改一个客户端地址,用户的工作副本切到另一个仓库路径后继续提交,历史记录完全一致,体验上非常接近原服务器,hooks双提交模式下,两个仓库各自维护独立的版本号,切换后版本号会对不上,容易造成混乱。
常见问题解答
svn一次提交两个服务器有哪些实现方式?
目前被广泛使用的方案只有两种:svnsync官方镜像同步和服务端post-commit钩子脚本,svnsync适合仓库级完整复制,脚本方式适合特定场景的定制化需求,二者可以同时使用,脚本触发同步并做额外处理。
svnsync同步后镜像仓库能直接提交代码吗?
不能,镜像仓库被svnsync初始化后,它的版本库属性带有特殊标记,直接提交会触发pre-revprop-change钩子并拒绝操作,这是设计如此,目的是防止镜像仓库与源仓库产生分叉,如果实在需要直接修改镜像仓库,只能先解除镜像关系,但那样同步链路也会断开,建议把镜像仓库当作只读灾备。
svn双服务器同步会丢失提交记录吗?
配置正确的情况下,svnsync是逐版本复制的,提交记录、作者、时间戳、日志信息都能完整保留,初步同步完成后建议用svn log对照两个仓库的版本号与提交记录来验证完整性,如果发现版本号不连续,多数是同步中途网络中断,重新执行同步命令即可补全。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/701994.html





