【轉載】rsync簡介:inotify + rsync

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),包括它的多線程也一樣矫户,如果有興趣片迅,可以自己寫一寫。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末皆辽,一起剝皮案震驚了整個濱河市柑蛇,隨后出現(xiàn)的幾起案子芥挣,更是在濱河造成了極大的恐慌,老刑警劉巖耻台,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件空免,死亡現(xiàn)場離奇詭異,居然都是意外死亡粘我,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門痹换,熙熙樓的掌柜王于貴愁眉苦臉地迎上來征字,“玉大人,你說我怎么就攤上這事娇豫〕捉” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵冯痢,是天一觀的道長氮昧。 經常有香客問我,道長浦楣,這世上最難降的妖魔是什么袖肥? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮振劳,結果婚禮上椎组,老公的妹妹穿的比我還像新娘。我一直安慰自己历恐,他們只是感情好寸癌,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弱贼,像睡著了一般蒸苇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吮旅,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天溪烤,我揣著相機與錄音,去河邊找鬼庇勃。 笑死氛什,一個胖子當著我的面吹牛,可吹牛的內容都是我干的匪凉。 我是一名探鬼主播枪眉,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼再层!你這毒婦竟也來了贸铜?” 一聲冷哼從身側響起堡纬,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒿秦,沒想到半個月后烤镐,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡棍鳖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年炮叶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渡处。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡镜悉,死狀恐怖,靈堂內的尸體忽然破棺而出医瘫,到底是詐尸還是另有隱情侣肄,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布醇份,位于F島的核電站稼锅,受9級特大地震影響,放射性物質發(fā)生泄漏僚纷。R本人自食惡果不足惜矩距,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怖竭。 院中可真熱鬧剩晴,春花似錦、人聲如沸侵状。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趣兄。三九已至绽左,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艇潭,已是汗流浹背拼窥。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹋凝,地道東北人鲁纠。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像鳍寂,于是被迫代替她去往敵國和親改含。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內容