轉自: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命令階段,不會進入到下一步的循環階段。