转自:http://www.cnblogs.com/f-ck-need-u/p/7220193.htmlhtml
若是要实现定时同步数据,能够在客户端将rsync加入定时任务,可是定时任务的同步时间粒度并不能达到实时同步的要求。在Linux kernel 2.6.13后提供了inotify文件系统监控机制。经过rsync+inotify组合能够实现实时同步。java
inotify实现工具备几款:inotify自己、sersync、lsyncd。其中sersync是金山的周洋开发的工具,克服了inotify的缺陷,且提供了几个插件做为可选工具。此处先介绍inotify的用法以及它的缺陷,经过其缺陷引出sersync,并介绍其用法。node
inotify由inotify-tools包提供。在安装inotify-tools以前,请确保内核版本高于2.6.13,且在/proc/sys/fs/inotify目录下有如下三项,这表示系统支持inotify监控,关于这3项的意义,下文会简单解释。git
[root@node1 tmp]# ll /proc/sys/fs/inotify/ total 0 -rw-r--r-- 1 root root 0 Feb 11 19:57 max_queued_events -rw-r--r-- 1 root root 0 Feb 11 19:57 max_user_instances -rw-r--r-- 1 root root 0 Feb 11 19:57 max_user_watches
epel源上提供了inotify-tools工具,或者下载源码包格式进行编译。github
inotify-tools源码包地址:https://cloud.github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gzweb
如下为编译安装过程:shell
tar xf inotify-tools-3.14.tar.gz ./configure --prefix=/usr/local/inotify-tools-3.14 make && make install ln -s /usr/local/inotify-tools-3.14 /usr/local/inotify
inotify-tools工具只提供了两个命令。vim
[root@xuexi ~]# rpm -ql inotify-tools | grep bin/ /usr/bin/inotifywait /usr/bin/inotifywatch
其中inotifywait命令用于等待文件发生变化,因此能够能够实现监控(watch)的功能,该命令是inotify的核心命令。inotifywatch用于收集文件系统的统计数据,例如发生了多少次inotify事件,某文件被访问了多少次等等,通常用不上。bash
如下是inotify相关的内核参数。服务器
(1)./proc/sys/fs/inotify/max_queued_events:调用inotify_init时分配到inotify instance中可排队的event数的最大值,超出值时的事件被丢弃,但会触发队列溢出Q_OVERFLOW事件。
(2)./proc/sys/fs/inotify/max_user_instances:每个real user可建立的inotify instances数量的上限。
(3)./proc/sys/fs/inotify/max_user_watches:每一个inotify实例相关联的watches的上限,即每一个inotify实例可监控的最大目录、文件数量。若是监控的文件数目巨大,须要根据状况适当增长此值。
如:
[root@xuexi ~]# echo 30000000 > /proc/sys/fs/inotify/max_user_watches
####1.2 inotifywait命令以及事件分析
inotifywait命令的选项:
-m:表示始终监控,不然应该是监控到了一次就退出监控了 -r:递归监控,监控目录中的任何文件,包括子目录。递归监控可能会超出max_user_watches的值,须要适当调整该值 @<file>:若是是对目录进行递归监控,则该选项用于排除递归目录中不被监控的文件。file是相对路径仍是绝对路径由监控目录是相对仍是绝对来决定 -q:--quiet的意思,静默监控,这样就不会输出一些无关的信息 -e:指定监控的事件。通常监控的就delete、create、attrib、modify、close_write --exclude <pattern> :经过模式匹配来指定不被监控的文件,区分大小写 --excludei <pattern>:经过模式匹配来指定不被监控的文件,不区分大小写 --timefmt:监控到事件触发后,输出的时间格式,可指定可不指定该选项,通常设置为[--timefmt '%Y/%m/%d %H:%M:%S'] --format:用户自定义的输出格式,如[--format '%w%f %e%T'] %w:产生事件的监控路径,不必定就是发生事件的具体文件,例如递归监控一个目录,该目录下的某文件产生事件,将输出该目录而非其内具体的文件 %f:若是监控的是一个目录,则输出产生事件的具体文件名。其余全部状况都输出空字符串 %e:产生的事件名称 %T:以"--timefmt"定义的时间格式输出当前时间,要求同时定义"--timefmt"
inotifywait -e可监控的事件:
access:文件被访问 modify:文件被写入 attrib:元数据被修改。包括权限、时间戳、扩展属性等等 close_write:打开的文件被关闭,是为了写文件而打开文件,以后被关闭的事件 close_nowrite:read only模式下文件被关闭,即只能是为了读取而打开文件,读取结束后关闭文件的事件 close:是close_write和close_nowrite的结合,不管是何种方式打开文件,只要关闭都属于该事件 open:文件被打开 moved_to:向监控目录下移入了文件或目录,也能够是监控目录内部的移动 moved_from:将监控目录下文件或目录移动到其余地方,也能够是在监控目录内部的移动 move:是moved_to和moved_from的结合 moved_self:被监控的文件或目录发生了移动,移动结束后将再也不监控此文件或目录 create:在被监控的目录中建立了文件或目录 delete:删除了被监控目录中的某文件或目录 delete_self:被监控的文件或目录被删除,删除以后再也不监控此文件或目录 umount:挂载在被监控目录上的文件系统被umount,umount后再也不监控此目录 isdir :监控目录相关操做
如下是几个示例:
[root@xuexi ~]# mkdir /longshuai [root@xuexi ~]# inotifywait -m /longshuai # 之前台方式监控目录,因为没指定监控的事件,因此监控全部事件 Setting up watches. Watches established.
打开其余会话,对被监控目录进行一些操做,查看各操做会触发什么事件。
[root@xuexi ~]# cd /longshuai # 进入目录不触发任何事件
(1).向目录中建立文件,触发create、open attrib、close_write和close事件。
[root@xuexi longshuai]# touch a.log /longshuai/ CREATE a.log /longshuai/ OPEN a.log /longshuai/ ATTRIB a.log /longshuai/ CLOSE_WRITE,CLOSE a.log
若是是建立目录,则触发的事件则少的多。
[root@xuexi longshuai]# mkdir b /longshuai/ CREATE,ISDIR b
ISDIR表示产生该事件的对象是一个目录。
(2).修改文件属性,触发attrib事件。
[root@xuexi longshuai]# chown 666 a.log /longshuai/ ATTRIB a.log
(3).cat查看文件,触发open、access、close_nowrite和close事件。
[root@xuexi longshuai]# cat a.log /longshuai/ OPEN a.log /longshuai/ ACCESS a.log /longshuai/ CLOSE_NOWRITE,CLOSE a.log
(4).向文件中追加或写入或清除数据,触发open、modify、close_write和close事件。
[root@xuexi longshuai]# echo "haha" >> a.log /longshuai/ OPEN a.log /longshuai/ MODIFY a.log /longshuai/ CLOSE_WRITE,CLOSE a.log
(5).vim打开文件并修改文件,中间涉及到临时文件,因此有很是多的事件。
[root@xuexi longshuai]# vim a.log /longshuai/ OPEN,ISDIR /longshuai/ CLOSE_NOWRITE,CLOSE,ISDIR /longshuai/ OPEN,ISDIR /longshuai/ CLOSE_NOWRITE,CLOSE,ISDIR /longshuai/ OPEN a.log /longshuai/ CREATE .a.log.swp /longshuai/ OPEN .a.log.swp /longshuai/ CREATE .a.log.swx /longshuai/ OPEN .a.log.swx /longshuai/ CLOSE_WRITE,CLOSE .a.log.swx /longshuai/ DELETE .a.log.swx /longshuai/ CLOSE_WRITE,CLOSE .a.log.swp /longshuai/ DELETE .a.log.swp /longshuai/ CREATE .a.log.swp /longshuai/ OPEN .a.log.swp /longshuai/ MODIFY .a.log.swp /longshuai/ ATTRIB .a.log.swp /longshuai/ CLOSE_NOWRITE,CLOSE a.log /longshuai/ OPEN a.log /longshuai/ CLOSE_NOWRITE,CLOSE a.log /longshuai/ MODIFY .a.log.swp /longshuai/ CREATE 4913 /longshuai/ OPEN 4913 /longshuai/ ATTRIB 4913 /longshuai/ CLOSE_WRITE,CLOSE 4913 /longshuai/ DELETE 4913 /longshuai/ MOVED_FROM a.log /longshuai/ MOVED_TO a.log~ /longshuai/ CREATE a.log /longshuai/ OPEN a.log /longshuai/ MODIFY a.log /longshuai/ CLOSE_WRITE,CLOSE a.log /longshuai/ ATTRIB a.log /longshuai/ ATTRIB a.log /longshuai/ MODIFY .a.log.swp /longshuai/ DELETE a.log~ /longshuai/ CLOSE_WRITE,CLOSE .a.log.swp /longshuai/ DELETE .a.log.swp
其中有"ISDIR"标识的是目录事件。此外,须要注意到vim过程当中,相应的几个临时文件(.swp、.swx和以~为后缀的备份文件)也产生了事件,这些临时文件的相关事件在实际应用过程当中,其实不该该被监控。
(6).向目录中拷入一个文件,触发create、open、modify和close_write、close事件。其实和新建文件基本相似。
[root@xuexi longshuai]# cp /bin/find . /longshuai/ CREATE find /longshuai/ OPEN find /longshuai/ MODIFY find /longshuai/ MODIFY find /longshuai/ CLOSE_WRITE,CLOSE find
(7).向目录中移入和移除一个文件。
[root@xuexi longshuai]# mv /tmp/after.log /longshuai /longshuai/ MOVED_TO after.log [root@xuexi longshuai]# mv /tmp/after.log /longshuai /longshuai/ MOVED_FROM after.log
(8).删除一个文件,触发delete事件。
[root@xuexi longshuai]# rm -f a.log /longshuai/ DELETE a.log
从上面的测试结果中能够发现,不少动做都涉及了close事件,且大多数状况都是伴随着close_write事件的。因此,大多数状况下在定义监控事件时,其实并不真的须要监控open、modify、close事件。特别是close,只需监控它的分支事件close_write和close_nowrite便可。因为通常状况下inotify都是为了监控文件的增删改,不会监控它的访问,因此通常只需监控close_write便可。
因为不少时候定义触发事件后的操做都是根据文件来判断的,例如a文件被监控到了变化(无论是什么变化),就当即执行操做A,又因为对文件的一个操做行为每每会触发多个事件,例如cat查看文件就触发了open、access、close_nowrite和close事件,这样极可能会由于多个事件被触发而重复执行操做A。例以下面的示例,当监控到了/var/log/messages文件中出现了a.log关键字,就执行echo动做。
while inotifywait -mrq -e modify /var/log/messages; do if tail -n1 /var/log/messages | grep a.log; then echo "haha" fi done
综合以上考虑,建议对监控对象的close_write、moved_to、moved_from、delete和isdir(主要是create,isdir,但没法定义这两个事件的总体,因此仅监控isdir)事件定义对应的操做,由于它们互不重复。若有须要,能够将它们分开定义,再添加须要监控的其余事件。例如:
[root@xuexi tmp]# cat a.sh #!/bin/bash # inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir /longshuai |\ while read line;do if grep -i delete $line; then echo "At `date +"%F %T"`: $line" >>/etc/delete.log else rsync -az $line --password-file=/etc/rsync_back.passwd rsync://rsync_backup@172.16.10.6::longshuai fi done
inotify是监控工具,监控目录或文件的变化,而后触发一系列的操做。
假若有一台站点发布服务器A,还有3台web服务器B/C/D,目的是让服务器A上存放站点的目录中有文件变化时,自动触发同步将它们推到web服务器上,这样可以让web服务器最快的获取到最新的文件。须要搞清楚的是,监控的是A上的目录,推送到的是B/C/D服务器,因此在站点发布服务器A上装好inotify工具。除此以外,通常还在web服务器BCD上将rsync配置为daemon运行模式,让其在873端口上处于监听状态(并不是必须,即便是sersync也非必须如此)。也就是说,对于rsync来讲,监控端是rsync的客户端,其余的是rsync的服务端。
固然,这只是最可能的使用状况,并不是必定须要如此。何况,inotify是独立的工具,它和rsync无关,它只是为rsync提供一种比较好的实时同步方式而已。
如下是监控/www目录的一个inotify+rsync脚本示例,也是网上流传的用法版本。但注意,该脚本很是烂,实际使用时须要作一些修改,此处仅仅只是示例(若是你不考虑资源消耗,那就无所谓)。
[root@xuexi www]# cat ~/inotify.sh #!/bin/bash watch_dir=/www push_to=172.16.10.5 inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%e:%T' $watch_dir \ --exclude=".*.swp" |\ while read line;do # logging some files which has been deleted and moved out if echo $line | grep -i -E "delete|moved_from";then echo "$line" >> /etc/inotify_away.log fi # from here, start rsync's function rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:/tmp if [ $? -eq 0 ];then echo "sent $watch_dir success" else echo "sent $watch_dir failed" fi done
而后对上面的脚本赋予执行权限并执行。注意,该脚本是用来前台测试运行的,若是要后台运行,则将最后一段的if字句删掉。
该脚本记录了哪些被删除或从监控目录中移出的文件,且监控到事件后,触发的rsync操做是对整个监控目录$watch_dir进行同步,而且不对vim产生的临时文件进行同步。
前台运行该监控脚本。
[root@xuexi www]# ~/inotify.sh
而后测试分别向监控目录/www下作拷贝文件、删除文件等操做。
例如删除文件,会先记录删除事件到/etc/inotify_away.log文件中,再执行rsync同步删除远端对应的文件。
/www/yum.repos.d/base.repo:DELETE:2017-07-21 14:47:46 sent /www success /www/yum.repos.d:DELETE,ISDIR:2017-07-21 14:47:46 sent /www success 再例如,拷入目录/etc/pki到/www下,会产生多个rsync结果。 sent /www success sent /www success sent /www success sent /www success sent /www success sent /www success ......
显然,因为拷入了多个文件,rsync被触发了屡次,但其实rsync只要同步一次/www目录到远端就足够了,多余的rsync操做彻底是浪费资源。若是拷入少许文件,其实无所谓,但若是拷入成千上万个文件,将长时间调用rsync。例如:
[root@xuexi www]# cp -a /usr/share/man /www
拷入了15000多个文件到www目录下,该脚本将循环15000屡次,且将调用15000屡次rsync。虽然通过一次目录同步以后,rsync的性能消耗很是低(即便性能浪费少,但仍然是浪费),但它消耗大量时间时间资源以及网络带宽。
虽然inotify已经整合到了内核中,在应用层面上也常拿来辅助rsync实现实时同步功能,可是inotify因其设计太过细致从而使得它配合rsync并不完美,因此须要尽量地改进inotify+rsync脚本或者使用sersync工具。另外,inotify存在bug。
当向监控目录下拷贝复杂层次目录(多层次目录中包含文件),或者向其中拷贝大量文件时,inotify常常会随机性地遗漏某些文件。这些遗漏掉的文件因为未被监控到,全部监控的后续操做都不会执行,例如不会被rsync同步。
实际上,inotifywait的man文档中也给出了这个bug说明。
BUGS
There are race conditions in the recursive directory watching code which can cause events to be missed if they occur in a directory immediately after that directory is created. This is probably not fixable.
为了说明这个bug的影响,如下给出一些示例来证实。
如下是一个监控delete和close_write事件的示例,监控的是/www目录,该目录下初始时没有pki目录。
[root@xuexi ~]# inotifywait -mrq -e delete,close_write --format '%w%f:%e' /www
监控开始后,准备向该目录下拷贝/etc/pki目录,该目录有多个子目录,且有多个层次的子目录,有一些文件分布在各个子目录下。通过汇总,/etc/pki目录下共有30个普通文件。
[root@xuexi www]# find /etc/pki/ -type f | wc -l 30
另开一个终端,拷贝pki目录到/www下。
[root@xuexi www]# cp -a /etc/pki /www
于此同时,在监控终端上将产生一些监控事件。
/www/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7:CLOSE_WRITE,CLOSE /www/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-7:CLOSE_WRITE,CLOSE /www/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Testing-7:CLOSE_WRITE,CLOSE /www/pki/tls/certs/Makefile:CLOSE_WRITE,CLOSE /www/pki/tls/certs/make-dummy-cert:CLOSE_WRITE,CLOSE /www/pki/tls/certs/renew-dummy-cert:CLOSE_WRITE,CLOSE /www/pki/tls/misc/c_info:CLOSE_WRITE,CLOSE /www/pki/tls/misc/c_issuer:CLOSE_WRITE,CLOSE /www/pki/tls/misc/c_name:CLOSE_WRITE,CLOSE /www/pki/tls/openssl.cnf:CLOSE_WRITE,CLOSE /www/pki/ca-trust/README:CLOSE_WRITE,CLOSE /www/pki/ca-trust/ca-legacy.conf:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/java/README:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/java/cacerts:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/pem/email-ca-bundle.pem:CLOSE_WRITE,CLOSE /www/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem:CLOSE_WRITE,CLOSE /www/pki/ca-trust/source/README:CLOSE_WRITE,CLOSE /www/pki/nssdb/cert8.db:CLOSE_WRITE,CLOSE /www/pki/nssdb/cert9.db:CLOSE_WRITE,CLOSE /www/pki/nssdb/key3.db:CLOSE_WRITE,CLOSE /www/pki/nssdb/key4.db:CLOSE_WRITE,CLOSE /www/pki/nssdb/pkcs11.txt:CLOSE_WRITE,CLOSE /www/pki/nssdb/secmod.db:CLOSE_WRITE,CLOSE
数一数上面监控到的事件结果,总共有25行,也就是25个文件的拷贝动做被监控到,但实际上拷贝的总文件数(目录和连接文件不归入计算)倒是30个。换句话说,inotify遗漏了5个文件。
通过测试,遗漏的数量和文件不是固定而是随机的(因此运气好可能不会有遗漏),且只有close_write事件会被遗漏,delete是没有问题的。为了证明这个bug,再举两个示例。
向监控目录/www下拷贝/usr/share/man目录,该目录下有15441个普通文件,且最深有3层子目录,层次上并不算复杂。
[root@xuexi www]# find /usr/share/man/ -type f | wc -l 15441
为了方便计算监控到的事件数量,将事件结果重定向到文件man.log中去。
[root@xuexi ~]# inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --format '%w%f:%e' /www > /tmp/man.log
开始拷贝。
[root@xuexi www]# cp -a /usr/share/man /www
拷贝结束后,统计/tmp/man.log文件中的行数,也即监控到的事件数。
[root@xuexi www]# cat /tmp/man.log | wc -l 15388
显然,监控到了15388个文件,比实际拷入的文件少了53个,这也证实了inotify的bug。
但上述两个示例监控的都是close_write事件,为了保证证实的严格性,监控全部事件,为了方便后续的统计,将监控结果重定向到pki.log文件中,且在监控开始以前,先删除/www/pki目录。
[root@xuexi ~]# rm -rf /www/pki [root@xuexi ~]# inotifywait -mrq --format '%w%f:%e' /www > /tmp/pki.log
向监控目录/www下拷贝/etc/pki目录。
[root@xuexi ~]# cp -a /etc/pki /www
因为监控了全部事件,因此目录相关事件"ISDIR"以及软连接相关事件也都被重定向到/tmp/pki.log中,为了统计监控到的文件数量,先把pki.log中目录相关的"ISDIR"行去掉,再对同一个文件进行去重,以便一个文件的多个事件只被统计一次,如下是统计命令。
[root@xuexi www]# sed /ISDIR/d /tmp/pki.log | cut -d":" -f1 | sort -u | wc -l 32
结果居然比普通文件数30多2个,实际并不是如此,由于pki.log中软连接文件也被统计了,但/www/pki目录下普通文件加上软连接文件总共有35个文件。
[root@xuexi www]# find /www/pki -type f -o -type l | wc -l 35
也就是说,即便监控的是所有事件,也仍是出现了遗漏,因此我认为这是inotify的一个bug。
可是须要说明的是,只有拷贝多层次包括多文件的目录时才会出现此bug,拷贝单个文件或简单无子目录的目录时不会出现此bug。对于inotify+rsync来讲,因为触发事件后常使用rsync同步整个目录而非单个文件,因此这个bug对rsync来讲并不算严重。
因为inotify的bug,使用inotify+rsync时应该老是让rsync同步目录,而不是同步那些产生事件的单个文件,不然极可能会出现文件遗漏。另外一方面,同步单个文件的性能很是差。下面相关缺陷的说明将默认rsync同步的是目录。
使用inotify+rsync时,考虑两方面问题:(1).因为inotify监控常常会对一个文件产生多个事件,且一次性操做同一个目录下多个文件也会产生多个事件,这使得inotify几乎老是屡次触发rsync同步目录,因为rsync同步的是目录,因此屡次触发rsync彻底不必,这会浪费资源和网络带宽;若是是分层次独立监控子目录,则会致使同步没法保证明时性(2).vim编辑文件的过程当中会产生.swp和.swx等临时文件,inotify也会监控这些临时文件,且临时文件会涉及多个事件,所以它们可能也会被rsync拷贝走,除非设置好排除临时文件,但不管如何,这些临时文件是不该该被同步的,极端状况下,同步vim的临时文件到服务器上多是致命的。
因为这两个缺陷,使得经过脚本实现的inotify+rsync几乎很难达到完美,即便要达到不错的完美度,也不是件容易的事(不要天真的认为网上那些inotify+rsync示例或者培训视频里老师给出的示例就是完美的,那些东西只能算是正确的囫囵吞枣式的用法示例)。总之,为了让inotify+rsync即能保证同步性能,又能保证不一样步临时文件,认真设计inotify+rsync的监控事件、循环以及rsync命令是颇有必要的。
在设计inotify+rsync脚本过程当中,有如下几个目标应该尽可能归入考虑或达到:
(1).每一个文件都尽可能少地产生监控事件,但又不能遗漏事件。
(2).让rsync同步目录,而不是同步产生事件的单个文件。
(3).一次性操做同步目录下的多个文件会产生多个事件,致使屡次触发rsync。若是能让这一批操做只触发一次rsync,则会大幅下降资源的消耗。
(4).rsync同步目录时,考虑好是否要排除某些文件,是否要加上"--delete"选项等。
(5).为了性能,能够考虑对子目录、对不一样事件单独设计inotify+rsync脚本。
之前文给出的示例脚原本分析。
[root@xuexi www]# cat ~/inotify.sh #!/bin/bash watch_dir=/www push_to=172.16.10.5 inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%e:%T' $watch_dir \ --exclude=".*.swp" |\ while read line;do # logging some files which has been deleted and moved out if echo $line | grep -i -E "delete|moved_from";then echo "$line" >> /etc/inotify_away.log fi # from here, start rsync's function rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:/tmp if [ $? -eq 0 ];then echo "sent $watch_dir success" else echo "sent $watch_dir failed" fi done
该脚本中已经尽可能少地设置监控事件,使得它尽可能少重复触发rsync。但须要明确的是,尽管设计的目标是尽可能少触发事件,但应该以知足需求为前提来定义监控事件。若是不清楚如何选择监控事件,回看前文inotify命令以及事件分析。另外,能够考虑对文件、目录、子目录单独定义不一样的脚本分别监控不一样事件。
该脚本的不足之处主要在于重复触发rsync。该脚本中rsync同步的是目录而非单个文件,因此若是一次性操做了该目录中多个文件,将会产生多个事件,也所以会触发屡次rsync命令,在前文中给出了一个拷贝/usr/share/man的示例,它调用了15000屡次rsync,其实只需同步一次便可,剩余的上万次同步彻底是多余的。
所以,上述脚本的改进方向是尽可能少地调用rsync,但却要保证rsync的实时性和同步完整性。使用sersync工具能够很轻松地实现这一点,也许sersync的做者开发该工具的最初目标也是为了解决这个问题。关于sersync的用法,留在后文介绍。
1.6 inotify+rsync的最佳实现
在上面已经提过inotify+rsync不足之处以及改进的目标。如下是经过修改shell脚原本改进inotify+rsync的示例。
[root@xuexi tmp]# cat ~/inotify.sh #!/bin/bash ########################################################### # description: inotify+rsync best practice # # author : 骏马金龙 # # blog : http://www.cnblogs.com/f-ck-need-u/ # ########################################################### watch_dir=/www push_to=172.16.10.5 # First to do is initial sync rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:/tmp inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%e:%T' $watch_dir \ --exclude=".*.swp" >>/etc/inotifywait.log & while true;do if [ -s "/etc/inotifywait.log" ];then grep -i -E "delete|moved_from" /etc/inotifywait.log >> /etc/inotify_away.log rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:/tmp if [ $? -ne 0 ];then echo "$watch_dir sync to $push_to failed at `date +"%F %T"`,please check it by manual" |\ mail -s "inotify+Rsync error has occurred" root@localhost fi cat /dev/null > /etc/inotifywait.log rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:/tmp else sleep 1 fi done
为了让一次性对目录下多个文件的操做只触发一次rsync,经过while read line这种读取标准输入的循环方式是不可能实现的。
本人的实现方法是将inotifywait获得的事件记录到文件/etc/inotifywait.log中,而后在死循环中判断该文件,若是该文件不为空则调用一次rsync进行同步,同步完后当即清空inotifywait.log文件,防止重复调用rsync。但须要考虑一种状况,inotifywait可能会不断地向inotifywait.log中写入数据,清空该文件可能会使得在rsync同步过程当中被inotifywait监控到的文件被rsync遗漏,因此在清空该文件后应该再调用一次rsync进行同步,这也变相地实现了失败重传的错误处理功能。若是没有监控到事件,inotifywait.log将是空文件,此时循环将睡眠1秒钟,因此该脚本并非百分百的实时,但1秒钟的偏差对于cpu消耗来讲是很值得的。
该脚本对每批事件只调用两次rsync,虽然没法像sersync同样只触发一次rsync,但差距彻底能够忽略不计。
另外,脚本中inotifywait命令中的后台符号"&"毫不能少,不然脚本将一直处于inotifywait命令阶段,不会进入到下一步的循环阶段。