基于rsync的开发环境代码传输

  现在很多的后台服务端开发者都不能享受本地电脑编码、本地编译调试了,一来为了兼顾沟通、办公等需求电脑都会装Windows系统,如果不是一个Geek氛围浓厚的开源公司很难Linux单系统走天下;二来现在的服务越来的越复杂,使得开发的模块很难在本地单独运行和调试;再则就是企业处于安全和管理的需要,办公网络和开发测试网络、生产网络会进行隔离,那么很有可能你的代码就必须传输到目标机器上进行编译和调试了。
  如果环境的网络拓扑比较简单的话,使用CIFS、NFS、SSHFS等网络文件系统就可以解决这个问题,但是如果网络环境比较复杂的话,比如中途给你设置一个跳板机,那上面的方法基本没辙了。之前了解到有很多同事当前都是ssh到目标机器上,然后直接在目标机器上用vim进行编码,如果这类开发者要么之前就习惯了使用vim用作IDE来开发,并且有一套压箱底的编辑器配置文件共享上去是很赞的,否则想想这种方式阅读和编码就格外蛋疼。
  我们要相信,程序员是最不喜欢做重复性体力劳动的。一问其他的同僚,大家给出的方案是inotify+rsync,自己一搜看来这个组合的确强大,其可以用于企业级平台下的文件备份、资源分发和更新等各种需要“同步”的需求。然后作为macOS工作机的我来说,自然要变通一下才可以。
  说到rsync这个工具使用起来比较简单的,但是前几天遇到一篇美团的博客,才得知这东西还是大有讲头的。rsync最求的是极致效率,为此设计出快速的差异监测和增量更新算法,从而减少网络占用并增加同步速度。由于需要差异比对,因此除了发起同步的源端外,还需要在同步目的端创建rsync服务:当同一个主机相同或者不同文件系统中使用rsync同步的话,操作系统会fork出目的端rsync进程,和源端进程使用管道进行通信;当需要跨主机同步的时候,源端通常通过SSH和目的端连接,然后源端向目的端发送命令创建一个rsync的进程,当然目的端也可以采用rsync服务守候进程的方式,自动侦听873端口。
  rsync最值得探究的就是其同步算法了。默认情况下rsync采用对文件的修改时间+文件大小的方式推断文件是否有差异,由于这种方式只需要检查对应目录的差异信息,因此速度是最快的,但是可能会产生误判!所以比较重要的环境可以添加–checksum参数,这种情况下rsync会采用若校验码+强校验码的方式来比对文件的内容进行差异分析。rsync会把每个文件分为一个个固定长度的chunk块,针对文件的每个部分首先计算adler-32的32比特弱校验码,如果两者不相等那么对应块必定不相同,会触发对应块的传输,否则会进行MD5强校验码的计算,以判断两者是否相同并决定是否触发传输(虽说MD5也可能产生弱碰撞,但是概率实在太低太低了)。通过这种方式可以实现文件差异的最搞笑同步传输,不过针对某些压缩文件,源文件的微小改动也会导致压缩结果文件的巨大差异,从而导致大量的同步负担,针对这种情况很多压缩工具会针对此种情况进行优化。
  所以rsync对于云计算环境下的高效同步操作应当是很重要的技术手段,比如针对网盘等应用,Dropbox的同步效率之所以这么高就得益于librsync的差异同步技术。衍生的rdiff工具可以针对修改前后的文件差异delta文件,此后将其用于目标旧文件就可以得到新文件,尤其对于二进制文件是十分适用的,这也让我想起了Fedora的delta安装文件的差异性更新了!而我前面提到的美团的那篇文章,是针对单点分发大量客户端需要同步的时候,如果服务端针对每个客户端都计算校验值很可能会将服务器拖垮,zsync的思路是服务端事先计算出文件的校验值供客户端下载,客户端通过sign对比本地文件寻找出差异,然后根据需要采用HTTP Range向服务端按需下载所需部分。不过如果客户端支持上传,那么事先起来就会相对来说比较麻烦了。还有,不同类型的文件修改形式也不一样,比如多媒体文件就不会局部位置的更新,而对于文档类型却很有可能;对于chuck划分,也不能因为中间插入某几个自己就导致后面所有校验值错位,不过我没看过Andrew Tridgell的论文,所以这里也不能知道其处理方式。
  rsync的wiki就摘录这么多了。通过rsync的-e参数,可以指明目的端同步shell的启动方式,在这里可以指明目的端同步shell的创建方式,通过配置证书免密码的登录方式,就具备了同步操作自动化的条件了。接下来我们既可以修改完代码后手动执行一次同步操作,或者定时进行同步操作外,但是最好能够在源文件发生实际修改后才触发同步操作,这就需要借助文件系统的异步事件来实现了。
  文件系统的异步机制在Linux平台就是大名鼎鼎的inotify,这使得很多响应文件变化的软件和服务得以高效的运行,而不必依据手动或者crontab定时器触发对应操作了,客户端inotifywait工具可以搭配任何的命令和脚本使用。

1
2
3
4
while true; do
inotifywait -r -e modify,attrib,close_write,move,create,delete /opt/syncfiles
rsync -avz -e "ssh -i /root/rsync-key -o StrictHostKeyChecking=no" /opt/syncfiles/ root@rsync-host02:/opt/syncfiles/
done

  由于inotify是Linux专用的,macOS平台必须另谋出路。选来选去,最终选了fswatch,这货算是文件系统事件侦听的跨平台封装工具,而macOS平台对应的机制是FSEvents。使用这个工具,找到对应的脚本命令是:

1
2
3
4
5
fswatch -o /opt/syncfiles 
| while read events
do
rsync -avz -e "ssh -i /root/rsync-key -o StrictHostKeyChecking=no" /opt/syncfiles/ root@rsync-host02:/opt/syncfiles/
done

  本来想写个脚本凑合着用的,但是Google偏偏让我发现了fswatch-rsync这个项目,该项目的作者有着相似的同步需求:客户机-跳板机-服务器的网络拓扑结构,然后需求客户机的文件能够经过跳板机自动同步到服务器上面。虽是简单的shell脚本,但是写的还是挺优雅规范的,于是我就直接于是将这个项目拷贝下来修改了一下就用上了(没用到跳板机特性),在实际的环境下测试一下,效果果真棒哒哒!
fswatch-rsync
  哈哈,开心的我又可以安心的写代码了!

参考