https://www.cnblogs.com/f-ck-need-u/p/7220193.html#auto_id_0
inotify+rsync
如果要實現(xiàn)定時同步數據形帮,可以在客戶端將 rsync 加入定時任務负懦,但是定時任務的同步時間粒度并不能達到實時同步的要求。在 Linux kernel 2.6.13 后提供了 inotify 文件系統(tǒng)監(jiān)控機制。通過 rsync+inotify 組合可以實現(xiàn)實時同步忧便。
inotify 實現(xiàn)工具有幾款:inotify 本身、sersync、lsyncd。其中 sersync 是金山的周洋開發(fā)的工具林螃,克服了 inotify 的缺陷,且提供了幾個插件作為可選工具俺泣。此處先介紹 inotify 的用法以及它的缺陷疗认,通過其缺陷引出 sersync完残,并介紹其用法。
安裝 inotify-tools
inotify 由 inotify-tools 包提供横漏。在安裝 inotify-tools 之前谨设,請確保內核版本高于 2.6.13,且在/proc/sys/fs/inotify 目錄下有以下三項缎浇,這表示系統(tǒng)支持 inotify 監(jiān)控扎拣,關于這 3 項的意義,下文會簡單解釋素跺。
[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 工具鹏秋,或者下載源碼包格式進行編譯。
inotify-tools 源碼包地址:https://cloud.github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
以下為編譯安裝過程:
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 工具只提供了兩個命令亡笑。
[root@xuexi ~]# rpm -ql inotify-tools | grep bin/
/usr/bin/inotifywait
/usr/bin/inotifywatch
其中 inotifywait 命令用于等待文件發(fā)生變化,所以可以可以實現(xiàn)監(jiān)控(watch)的功能横朋,該命令是 inotify 的核心命令仑乌。inotifywatch 用于收集文件系統(tǒng)的統(tǒng)計數據,例如發(fā)生了多少次 inotify 事件琴锭,某文件被訪問了多少次等等晰甚,一般用不上。
以下是 inotify 相關的內核參數决帖。
(1)./proc/sys/fs/inotify/max_queued_events:調用 inotify_init 時分配到 inotify instance 中可排隊的 event 數的最大值厕九,超出值時的事件被丟棄,但會觸發(fā)隊列溢出 Q_OVERFLOW 事件地回。
(2)./proc/sys/fs/inotify/max_user_instances:每一個 real user 可創(chuàng)建的 inotify instances 數量的上限扁远。
(3)./proc/sys/fs/inotify/max_user_watches:每個 inotify 實例相關聯(lián)的 watches 的上限,即每個 inotify 實例可監(jiān)控的最大目錄刻像、文件數量畅买。如果監(jiān)控的文件數目巨大,需要根據情況適當增加此值细睡。
inotifywait 命令以及事件分析
inotifywait 命令的選項:
-m:表示始終監(jiān)控谷羞,否則應該是監(jiān)控到了一次就退出監(jiān)控了
-r:遞歸監(jiān)控,監(jiān)控目錄中的任何文件溜徙,包括子目錄湃缎。遞歸監(jiān)控可能會超出max_user_watches的值,需要適當調整該值
@<file>:如果是對目錄進行遞歸監(jiān)控蠢壹,則該選項用于排除遞歸目錄中不被監(jiān)控的文件嗓违。file是相對路徑還是絕對路徑由監(jiān)控目錄是相對還是絕對來決定
-q:--quiet的意思,靜默監(jiān)控知残,這樣就不會輸出一些無關的信息
-e:指定監(jiān)控的事件靠瞎。一般監(jiān)控的就delete比庄、create、attrib乏盐、modify佳窑、close_write
--exclude <pattern> :通過模式匹配來指定不被監(jiān)控的文件,區(qū)分大小寫
--excludei <pattern>:通過模式匹配來指定不被監(jiān)控的文件父能,不區(qū)分大小寫
--timefmt:監(jiān)控到事件觸發(fā)后神凑,輸出的時間格式,可指定可不指定該選項何吝,一般設置為[--timefmt '%Y/%m/%d %H:%M:%S']
--format:用戶自定義的輸出格式溉委,如[--format '%w%f %e%T']
%w:產生事件的監(jiān)控路徑,不一定就是發(fā)生事件的具體文件爱榕,例如遞歸監(jiān)控一個目錄瓣喊,該目錄下的某文件產生事件,將輸出該目錄而非其內具體的文件
%f:如果監(jiān)控的是一個目錄黔酥,則輸出產生事件的具體文件名藻三。其他所有情況都輸出空字符串
%e:產生的事件名稱
%T:以"--timefmt"定義的時間格式輸出當前時間,要求同時定義"--timefmt"
inotifywait -e 可監(jiān)控的事件:
access:文件被訪問
modify:文件被寫入
attrib:元數據被修改跪者。包括權限棵帽、時間戳、擴展屬性等等
close_write:打開的文件被關閉渣玲,是為了寫文件而打開文件逗概,之后被關閉的事件
close_nowrite:read only模式下文件被關閉,即只能是為了讀取而打開文件忘衍,讀取結束后關閉文件的事件
close:是close_write和close_nowrite的結合逾苫,無論是何種方式打開文件,只要關閉都屬于該事件
open:文件被打開
moved_to:向監(jiān)控目錄下移入了文件或目錄枚钓,也可以是監(jiān)控目錄內部的移動
moved_from:將監(jiān)控目錄下文件或目錄移動到其他地方隶垮,也可以是在監(jiān)控目錄內部的移動
move:是moved_to和moved_from的結合
moved_self:被監(jiān)控的文件或目錄發(fā)生了移動,移動結束后將不再監(jiān)控此文件或目錄
create:在被監(jiān)控的目錄中創(chuàng)建了文件或目錄
delete:刪除了被監(jiān)控目錄中的某文件或目錄
delete_self:被監(jiān)控的文件或目錄被刪除秘噪,刪除之后不再監(jiān)控此文件或目錄
umount:掛載在被監(jiān)控目錄上的文件系統(tǒng)被umount狸吞,umount后不再監(jiān)控此目錄
isdir :監(jiān)控目錄相關操作
以下是幾個示例:
[root@xuexi ~]# mkdir /longshuai
[root@xuexi ~]# inotifywait -m /longshuai # 以前臺方式監(jiān)控目錄,由于沒指定監(jiān)控的事件指煎,所以監(jiān)控所有事件
Setting up watches.
Watches established.
打開其他會話蹋偏,對被監(jiān)控目錄進行一些操作,查看各操作會觸發(fā)什么事件至壤。
[root@xuexi ~]# cd /longshuai # 進入目錄不觸發(fā)任何事件
(1).向目錄中創(chuàng)建文件威始,觸發(fā) 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
如果是創(chuàng)建目錄晋渺,則觸發(fā)的事件則少的多。
[root@xuexi longshuai]# mkdir b
/longshuai/ CREATE,ISDIR b
ISDIR 表示產生該事件的對象是一個目錄脓斩。
(2).修改文件屬性木西,觸發(fā) attrib 事件。
[root@xuexi longshuai]# chown 666 a.log
/longshuai/ ATTRIB a.log
(3).cat 查看文件随静,觸發(fā) 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).向文件中追加或寫入或清除數據恋捆,觸發(fā) 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 和以~為后綴的備份文件)也產生了事件火焰,這些臨時文件的相關事件在實際應用過程中,其實不應該被監(jiān)控胧沫。
(6).向目錄中拷入一個文件昌简,觸發(fā) 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 /longshuai/after.log /tmp
/longshuai/ MOVED_FROM after.log
(8).刪除一個文件,觸發(fā) delete 事件六剥。
[root@xuexi longshuai]# rm -f a.log
/longshuai/ DELETE a.log
從上面的測試結果中可以發(fā)現(xiàn)晚顷,很多動作都涉及了 close 事件戚丸,且大多數情況都是伴隨著 close_write 事件的遵倦。所以,大多數情況下在定義監(jiān)控事件時地啰,其實并不真的需要監(jiān)控 open策彤、modify栓袖、close 事件匣摘。特別是 close,只需監(jiān)控它的分支事件 close_write 和 close_nowrite 即可裹刮。由于一般情況下 inotify 都是為了監(jiān)控文件的增刪改音榜,不會監(jiān)控它的訪問,所以一般只需監(jiān)控 close_write 即可必指。
由于很多時候定義觸發(fā)事件后的操作都是根據文件來判斷的囊咏,例如 a 文件被監(jiān)控到了變化(不管是什么變化),就立即執(zhí)行操作 A塔橡,又由于對文件的一個操作行為往往會觸發(fā)多個事件梅割,例如 cat 查看文件就觸發(fā)了 open、access葛家、close_nowrite 和 close 事件户辞,這樣很可能會因為多個事件被觸發(fā)而重復執(zhí)行操作 A。例如下面的示例癞谒,當監(jiān)控到了/var/log/messages 文件中出現(xiàn)了 a.log 關鍵字底燎,就執(zhí)行 echo 動作。
while inotifywait -mrq -e modify /var/log/messages; do
if tail -n1 /var/log/messages | grep a.log; then
echo "haha"
fi
done
綜合以上考慮弹砚,建議對監(jiān)控對象的 close_write双仍、moved_to、moved_from桌吃、delete 和 isdir(主要是 create,isdir朱沃,但無法定義這兩個事件的整體,所以僅監(jiān)控 isdir)事件定義對應的操作茅诱,因為它們互不重復逗物。如有需要,可以將它們分開定義瑟俭,再添加需要監(jiān)控的其他事件翎卓。例如:
[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 echo $line | grep -i delete &>/dev/null; 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 應該裝在哪里
inotify 是監(jiān)控工具,監(jiān)控目錄或文件的變化摆寄,然后觸發(fā)一系列的操作失暴。
假如有一臺站點發(fā)布服務器 A,還有 3 臺 web 服務器 B/C/D微饥,目的是讓服務器 A 上存放站點的目錄中有文件變化時锐帜,自動觸發(fā)同步將它們推到 web 服務器上,這樣能夠讓 web 服務器最快的獲取到最新的文件畜号。需要搞清楚的是缴阎,監(jiān)控的是 A 上的目錄,推送到的是 B/C/D 服務器简软,所以在站點發(fā)布服務器 A 上裝好 inotify 工具蛮拔。除此之外述暂,一般還在 web 服務器 BCD 上將 rsync 配置為 daemon 運行模式,讓其在 873 端口上處于監(jiān)聽狀態(tài)(并非必須建炫,即使是 sersync 也非必須如此)畦韭。也就是說,對于 rsync 來說肛跌,監(jiān)控端是 rsync 的客戶端艺配,其他的是 rsync 的服務端。
當然衍慎,這只是最可能的使用情況转唉,并非一定需要如此。況且稳捆,inotify 是獨立的工具赠法,它和 rsync 無關,它只是為 rsync 提供一種比較好的實時同步方式而已乔夯。
inotify+rsync 示例腳本(不完善)
以下是監(jiān)控/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" &>/dev/null;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
然后對上面的腳本賦予執(zhí)行權限并執(zhí)行。注意剃幌,該腳本是用來前臺測試運行的,如果要后臺運行晾浴,則將最后一段的 if 字句刪掉负乡。
該腳本記錄了哪些被刪除或從監(jiān)控目錄中移出的文件,且監(jiān)控到事件后脊凰,觸發(fā)的 rsync 操作是對整個監(jiān)控目錄 $watch_dir 進行同步抖棘,并且不對 vim 產生的臨時文件進行同步。
前臺運行該監(jiān)控腳本狸涌。
[root@xuexi www]# ~/inotify.sh
然后測試分別向監(jiān)控目錄/www 下做拷貝文件切省、刪除文件等操作。
例如刪除文件帕胆,會先記錄刪除事件到/etc/inotify_away.log 文件中朝捆,再執(zhí)行 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 被觸發(fā)了多次蝴乔,但其實 rsync 只要同步一次/www 目錄到遠端就足夠了,多余的 rsync 操作完全是浪費資源驮樊。如果拷入少量文件薇正,其實無所謂,但如果拷入成千上萬個文件囚衔,將長時間調用 rsync挖腰。例如:
[root@xuexi www]# cp -a /usr/share/man /www
拷入了 15000 多個文件到 www 目錄下,該腳本將循環(huán) 15000 多次佳魔,且將調用 15000 多次 rsync曙聂。雖然經過一次目錄同步之后,rsync 的性能消耗非常低(即使性能浪費少鞠鲜,但仍然是浪費)宁脊,但它消耗大量時間時間資源以及網絡帶寬。
inotify 的不足之處
雖然 inotify 已經整合到了內核中贤姆,在應用層面上也常拿來輔助 rsync 實現(xiàn)實時同步功能榆苞,但是 inotify 因其設計太過細致從而使得它配合 rsync 并不完美,所以需要盡可能地改進 inotify+rsync 腳本或者使用 sersync 工具霞捡。另外坐漏,inotify 存在 bug。
inotify 的 bug
當向監(jiān)控目錄下拷貝復雜層次目錄(多層次目錄中包含文件)碧信,或者向其中拷貝大量文件時赊琳,inotify 經常會隨機性地遺漏某些文件。這些遺漏掉的文件由于未被監(jiān)控到砰碴,所有監(jiān)控的后續(xù)操作都不會執(zhí)行躏筏,例如不會被 rsync 同步。
實際上呈枉,上面描述的問題不是 inotify 的缺陷趁尼,而是 inotify-tools 包中 inotifywait 工具的缺陷。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.
也就是說酥泞,那些直接發(fā)起 inotify 相關系統(tǒng)調用的上層工具(如 sersync、lsyncd 等)可能不會出現(xiàn)這個 bug啃憎。
為了說明這個 bug 的影響芝囤,以下給出一些示例來證明。
以下是一個監(jiān)控 delete 和 close_write 事件的示例,監(jiān)控的是/www 目錄凡人,該目錄下初始時沒有 pki 目錄名党。
[root@xuexi ~]# inotifywait -mrq -e delete,close_write --format '%w%f:%e' /www
監(jiān)控開始后,準備向該目錄下拷貝/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
于此同時,在監(jiān)控終端上將產生一些監(jiān)控事件冈在。
/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
數一數上面監(jiān)控到的事件結果倒慧,總共有 25 行,也就是 25 個文件的拷貝動作被監(jiān)控到包券,但實際上拷貝的總文件數(目錄和鏈接文件不納入計算)卻是 30 個纫谅。換句話說,inotify 遺漏了 5 個文件溅固。
經過測試付秕,遺漏的數量和文件不是固定而是隨機的(所以運氣好可能不會有遺漏),且只有 close_write 事件會被遺漏侍郭,delete 是沒有問題的询吴。為了證實這個 bug,再舉兩個示例亮元。
向監(jiān)控目錄/www 下拷貝/usr/share/man 目錄猛计,該目錄下有 15441 個普通文件,且最深有 3 層子目錄爆捞,層次上并不算復雜奉瘤。
[root@xuexi www]# find /usr/share/man/ -type f | wc -l
15441
為了方便計算監(jiān)控到的事件數量,將事件結果重定向到文件 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
拷貝結束后望艺,統(tǒng)計/tmp/man.log 文件中的行數苛秕,也即監(jiān)控到的事件數。
[root@xuexi www]# cat /tmp/man.log | wc -l
15388
顯然找默,監(jiān)控到了 15388 個文件艇劫,比實際拷入的文件少了 53 個,這也證明了 inotify 的 bug惩激。
但上述兩個示例監(jiān)控的都是 close_write 事件店煞,為了保證證明的嚴格性蟹演,監(jiān)控所有事件,為了方便后續(xù)的統(tǒng)計顷蟀,將監(jiān)控結果重定向到 pki.log 文件中酒请,且在監(jiān)控開始之前,先刪除/www/pki 目錄鸣个。
[root@xuexi ~]# rm -rf /www/pki
[root@xuexi ~]# inotifywait -mrq --format '%w%f:%e' /www > /tmp/pki.log
向監(jiān)控目錄/www 下拷貝/etc/pki 目錄羞反。
[root@xuexi ~]# cp -a /etc/pki /www
由于監(jiān)控了所有事件,所以目錄相關事件"ISDIR"以及軟鏈接相關事件也都被重定向到/tmp/pki.log 中囤萤,為了統(tǒng)計監(jiān)控到的文件數量昼窗,先把 pki.log 中目錄相關的"ISDIR"行去掉,再對同一個文件進行去重涛舍,以便一個文件的多個事件只被統(tǒng)計一次澄惊,以下是統(tǒng)計命令。
[root@xuexi www]# sed /ISDIR/d /tmp/pki.log | cut -d":" -f1 | sort -u | wc -l
32
結果竟然比普通文件數 30 多 2 個富雅,實際并非如此掸驱,因為 pki.log 中軟鏈接文件也被統(tǒng)計了,但/www/pki 目錄下普通文件加上軟鏈接文件總共有 35 個文件吹榴。
[root@xuexi www]# find /www/pki -type f -o -type l | wc -l
35
也就是說亭敢,即使監(jiān)控的是全部事件,也還是出現(xiàn)了遺漏图筹,所以我認為這是 inotify 的一個 bug帅刀。
但是需要說明的是,只有拷貝多層次包括多文件的目錄時才會出現(xiàn)此 bug远剩,拷貝單個文件或簡單無子目錄的目錄時不會出現(xiàn)此 bug扣溺。對于 inotify+rsync 來說,由于觸發(fā)事件后常使用 rsync 同步整個目錄而非單個文件瓜晤,所以這個 bug 對 rsync 來說并不算嚴重锥余。
inotify+rsync 的缺陷
由于 inotify 的 bug,使用 inotify+rsync 時應該總是讓 rsync 同步目錄痢掠,而不是同步那些產生事件的單個文件驱犹,否則很可能會出現(xiàn)文件遺漏。另一方面足画,同步單個文件的性能非常差雄驹。下面相關缺陷的說明將默認 rsync 同步的是目錄。
使用 inotify+rsync 時淹辞,考慮兩方面問題:(1).由于 inotify 監(jiān)控經常會對一個文件產生多個事件医舆,且一次性操作同一個目錄下多個文件也會產生多個事件,這使得 inotify 幾乎總是多次觸發(fā) rsync 同步目錄,由于 rsync 同步的是目錄蔬将,所以多次觸發(fā) rsync 完全沒必要爷速,這會浪費資源和網絡帶寬;如果是分層次獨立監(jiān)控子目錄霞怀,則會導致同步無法保證實時性(2).vim 編輯文件的過程中會產生.swp 和.swx 等臨時文件惫东,inotify 也會監(jiān)控這些臨時文件,且臨時文件會涉及多個事件毙石,因此它們可能也會被 rsync 拷貝走凿蒜,除非設置好排除臨時文件,但無論如何胁黑,這些臨時文件是不應該被同步的废封,極端情況下,同步 vim 的臨時文件到服務器上可能是致命的丧蘸。
由于這兩個缺陷漂洋,使得通過腳本實現(xiàn)的 inotify+rsync 幾乎很難達到完美,即使要達到不錯的完美度力喷,也不是件容易的事(不要天真的認為網上那些 inotify+rsync 示例或者培訓視頻里老師給出的示例就是完美的刽漂,那些東西只能算是正確的囫圇吞棗式的用法示例)〉苊希總之贝咙,為了讓 inotify+rsync 即能保證同步性能,又能保證不同步臨時文件拂募,認真設計 inotify+rsync 的監(jiān)控事件庭猩、循環(huán)以及 rsync 命令是很有必要的。
在設計 inotify+rsync 腳本過程中陈症,有以下幾個目標應該盡量納入考慮或達到:
(1).每個文件都盡量少地產生監(jiān)控事件蔼水,但又不能遺漏事件。
(2).讓 rsync 同步目錄录肯,而不是同步產生事件的單個文件趴腋。
(3).一次性操作同步目錄下的多個文件會產生多個事件,導致多次觸發(fā) rsync论咏。如果能讓這一批操作只觸發(fā)一次 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" &>/dev/null;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
該腳本中已經盡量少地設置監(jiān)控事件贬芥,使得它盡量少重復觸發(fā) rsync。但需要明確的是宣决,盡管設計的目標是盡量少觸發(fā)事件蘸劈,但應該以滿足需求為前提來定義監(jiān)控事件。如果不清楚如何選擇監(jiān)控事件尊沸,回看前文 inotify 命令以及事件分析威沫。另外,可以考慮對文件洼专、目錄棒掠、子目錄單獨定義不同的腳本分別監(jiān)控不同事件。
該腳本的不足之處主要在于重復觸發(fā) rsync屁商。該腳本中 rsync 同步的是目錄而非單個文件烟很,所以如果一次性操作了該目錄中多個文件,將會產生多個事件蜡镶,也因此會觸發(fā)多次 rsync 命令雾袱,在前文中給出了一個拷貝/usr/share/man 的示例,它調用了 15000 多次 rsync官还,其實只需同步一次即可芹橡,剩余的上萬次同步完全是多余的。
因此望伦,上述腳本的改進方向是盡量少地調用 rsync林说,但卻要保證 rsync 的實時性和同步完整性。使用 sersync 工具可以很輕松地實現(xiàn)這一點屯伞,也許 sersync 的作者開發(fā)該工具的最初目標也是為了解決這個問題述么。關于 sersync 的用法,留在后文介紹愕掏。
inotify+rsync 的最佳實現(xiàn)
在上面已經提過 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
為了讓一次性對目錄下多個文件的操作只觸發(fā)一次 rsync饵撑,通過 while read line 這種讀取標準輸入的循環(huán)方式是不可能實現(xiàn)的剑梳。
本人的實現(xiàn)方法是將 inotifywait 得到的事件記錄到文件/etc/inotifywait.log 中,然后在死循環(huán)中判斷該文件滑潘,如果該文件不為空則調用一次 rsync 進行同步垢乙,同步完后立即清空 inotifywait.log 文件,防止重復調用 rsync语卤。但需要考慮一種情況追逮,inotifywait 可能會不斷地向 inotifywait.log 中寫入數據酪刀,清空該文件可能會使得在 rsync 同步過程中被 inotifywait 監(jiān)控到的文件被 rsync 遺漏,所以在清空該文件后應該再調用一次 rsync 進行同步钮孵,這也變相地實現(xiàn)了失敗重傳的錯誤處理功能骂倘。如果沒有監(jiān)控到事件,inotifywait.log 將是空文件巴席,此時循環(huán)將睡眠 1 秒鐘历涝,所以該腳本并不是百分百的實時,但 1 秒鐘的誤差對于 cpu 消耗來說是很值得的漾唉。
該腳本對每批事件只調用兩次 rsync荧库,雖然無法像 sersync 一樣只觸發(fā)一次 rsync,但差距完全可以忽略不計赵刑。
另外分衫,腳本中 inotifywait 命令中的后臺符號"&"絕不能少,否則腳本將一直處于 inotifywait 命令階段般此,不會進入到下一步的循環(huán)階段丐箩。但需要注意,腳本中(子 shell)的后臺進程在腳本結束的時候不會隨之停止恤煞,而是掛靠在 pid=1 的 init/systemd 進程下屎勘,這種情況下可以直接使用 killall script_file 的方式來停止腳本,這樣腳本中的后臺也會中斷居扒。如果想直接在腳本中實現(xiàn)這樣的功能概漱,見:如何讓 shell 腳本自殺。
實際上喜喂,上面的腳本還遠不算完美瓤摧,更完美的做法是提供一個判斷功能,如果監(jiān)控的目錄非常大(即文件數量非常多)玉吁,應該傳輸變化的單個文件而不是同步整個目錄照弥,如果監(jiān)控目錄不算大,可以考慮同步整個目錄进副。sersync 采用的方式是監(jiān)控目錄这揣,但取出發(fā)生變化的單個文件并同步,并提供定時任務來決定多久整體同步一次影斑,也就是整個目錄同步给赞。這一切都能通過 shell 腳本來實現(xiàn),包括它的多線程也一樣矫户,如果有興趣片迅,可以自己寫一寫。