inotify + rsync实现linux文件实时同步,使用触发同步机制

时间:2021-06-03 15:13:50
inotify + rsync实现linux文件实时同步,使用触发同步机制
公司一套系统的同步使用的donotify,不能实现子目录的实时同步,通过查资料,发现inotify可以实现子目录的实时同步,以下为笔记。
一、介绍Inotify 是文件系统事件监控机制,作为 dnotify 的有效替代。dnotify 是较早内核支持的文件监控机制。Inotify 是一种强大的、细粒度的、异步的机制,它满足各种各样的文件监控需要,不仅限于安全和性能。
inotify 可以监视的文件系统事件包括:IN_ACCESS,即文件被访问IN_MODIFY,文件被 writeIN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等IN_CLOSE_WRITE,可写文件被 closeIN_CLOSE_NOWRITE,不可写文件被 closeIN_OPEN,文件被 openIN_MOVED_FROM,文件被移走,如 mvIN_MOVED_TO,文件被移来,如 mv、cpIN_CREATE,创建新文件IN_DELETE,文件被删除,如 rmIN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己IN_UNMOUNT,宿主文件系统被 umountIN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)注:上面所说的文件也包括目录。 

二、为能在shell下使用inotify特性,需要安装inotify-tools
1、inotify-tools:The general purpose of this package is to allow inotify's features to be used from within shell scripts.
下载地址:http://inotify-tools.sourceforge.net/
编译安装./configuremakemake install完成后,注意查看manpage,man inotify 、 man inotifywait

  • inotifywait 仅执行阻塞,等待 inotify 事件。您可以监控任何一组文件和目录,或监控整个目录树(目录、子目录、子目录的子目录等等)。在 shell 脚本中使用 inotifywait
  • inotifywatch 收集关于被监视的文件系统的统计数据,包括每个 inotify 事件发生多少次。

  • 2、inotify的系统相关参数: /proc interfaces       The following interfaces can be used to limit the amount of kernel memory consumed by inotify:
           /proc/sys/fs/inotify/max_queued_events              The value in this file is used when an application calls inotify_init(2) to set an upper  limit  on  the number  of  events  that  can be queued to the corresponding inotify instance.  Events in excess of this limit are dropped, but an IN_Q_OVERFLOW event is always generated.
           /proc/sys/fs/inotify/max_user_instances              This specifies an upper limit on the number of inotify instances that can be created per real user ID.
           /proc/sys/fs/inotify/max_user_watches              This specifies a limit on the number of watches that can be associated with each inotify instance.

    3、inotifywait 相关的参数(更多,查看manpage):inotifywaitThis command simply blocks for inotify events, making it appropriate for use in shell scripts. It can watch any set of files and directories, and can recursively watch entire directory trees.-m, --monitor              Instead  of  exiting  after receiving a single event, execute indefinitely.  The default behaviour is to exit after the first event occurs.-r, --recursive              Watch all subdirectories of any directories passed as arguments.  Watches will be set up recursively  to an  unlimited  depth.   Symbolic  links  are  not 
    traversed.  Newly created subdirectories will also be watched.-q, --quiet              If specified once, the program will be less verbose.  Specifically, it will not state when it  has  completed establishing all inotify watches. -e <event>, --event <event>              Listen for specific event(s) only.  The events which can be listened for are listed in the  EVENTS  section.  This option can be specified more than once.  If omitted, all events are listened for. use“,”separate multi events

    三、使用1.查看是否支持inotify,从kernel 2.6.13开始正式并入内核,RHEL5已经支持。看看是否有 /proc/sys/fs/inotify/目录,以确定内核是否支持inotify[root@RHEL5 Rsync]# ll /proc/sys/fs/inotifytotal 0-rw-r--r-- 1 root root 0 Oct  9 09:36 max_queued_events-rw-r--r-- 1 root root 0 Oct  9 09:36 max_user_instances-rw-r--r-- 1 root root 0 Oct  9 09:36 max_user_watches
    2.关于递归:inotifywaitThis command simply blocks for inotify events, making it appropriate for use in shell scripts. It can watch any set of files and directories, and can recursively watch entire directory trees.

    3.使用:#!/bin/shsrc=/opt/webmaildes=/tmpip=192.168.7.192
    /usr/local/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format  '%T %w%f' \ -e modify,delete,create,attrib \${src} \| while read  file        do                rsync -avz --delete --progress ${src} root@${ip}:${des} &&                echo "${src} was rsynced"                echo "---------------------------------------------------------------------------"        done注:当要排出同步某个目录时,为rsync添加--exculde=PATTERN参数,注意,路径是相对路径。详细查看man rsync当要排除都某个目录的事件监控的处理时,为inotifywait添加--exclude或--excludei参数。详细查看man inotifywait
    另:/usr/local/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format  '%T %w%f' \ -e modify,delete,create,attrib \${src} \上面的命令返回的值类似于:10/03/09 15:31 /wwwpic/1
    这3个返回值做为参数传给read,关于此处,有人是这样写的:inotifywait -mrq -e create,move,delete,modify $SRC | while read D E F;do
    细化了返回值。
    注:要取得监控文件发生的事件,在--format处指定%e参数,同时,使用--event参数来指定要监控的事件即可,如--format  '%T %w%f %e' --event modify,delete,create,attrib

    说明:当文件系统发现指定目录下有如上的条件的时候就触发相应的指令,是一种主动告之的而非我用循环比较目录下的文件的异动,该程序在运行时,更改目录内的文件时系统内核会发送一个信号,这个信号会触发运行rsync命令,这时会同步源目录和目标目录。--timefmt:指定输出时的输出格式  
    --format:  '%T %w%f'指定输出的格式,上面的输出类似于:12/10/08 06:34 /opt/webmail/dovecot-1.1.2/src/test/1


    小脚本,同步到多台主机:
    inotify + rsync实现linux文件实时同步,使用触发同步机制
    文件: inotify_rsync.tar.gz
    大小: 1KB
    下载: 下载

    更改后,更简单,适用于同步到相同的目录,监控多目录,多文件,同步到多台服务器
    #!/bin/sh#set -x
    #var src="/usr/local/nginx/html/lib /usr/local/nginx/html/www /usr/local/nginx/html/var/www.work.com.conf.php"des_ip="172.18.1.35 172.18.1.36 172.18.1.37 172.18.1.38"

    #functioninotify_fun (){/usr/local/bin/inotifywait -mrq --timefmt '%d/%m/%y-%H:%M' --format '%T %w%f' \-e modify,delete,create,move $1|while read time filedofor ip in $des_ipdoecho "`date +%Y%m%d-%T`: rsync -avzq --delete --progress $1 $ip:`dirname $1`"rsync -avzq --delete --progress $1 $ip:`dirname $1`echodonedone}
    #mainfor a in $srcdoinotify_fun $a & done




    参考:http://www.ibm.com/developerworks/cn/linux/l-ubuntu-inotify/index.html

    补充:经网友提示,有另外两个解决方案,大家可以参考一下:1:google开源项目Openduckbill(依赖于
    inotify- tools)
    http://code.google.com/p/openduckbill/ 2:sersync
    http://code.google.com/p/sersync/

    关于减少rsync的遍历,未完:
    考虑到被监测的目录每次有一下时间时都会触发rsync,modify,delete,create,move每次rsync都会遍历源目录,当被监测目录内文件特别多时,会造成系统资源的严重消耗,所以,让rsync每次只同步修改的文件。
    因为,如果从监控目录mv走一个目录,那么rsync只会报告找不到你移走的目录而无法删除备份机的应该删除的目录。
    所以,对于删除这个事件,没有办法了,只能同步被删除文件或目录的上级目录了。
    将事件分为两部分,modify,create,move事件,触发rsync,只同步修改了的文件。
    delete事件,同步被删除文件或目录的上级目录(不能越过要同步的根目录)。
    关于脚本内容的一些说明:rsync.conf里的目录格式一定要注意,没有最后的“/"
    boot.sh对于rsync命令的目标地址,是由两部分组成的:1、rsync.conf里的destdes=`grep '^dest' ${basedir}/rsync.conf| cut -d '=' -f 2 `2、被修改了的文件的完全路径,去掉源目录部分,去掉被修改的文件的文件名。mb=`echo $file|awk -F "/" '{NF=NF-1;OFS="/";print $0}'|sed "s#$src##g"`



    boot.sh#######################################################################################先做个记录,脚本在delete部分还有问题#!/bin/sh
    #basedir=/home/jason/RsyncdestNum=`grep -c '^dest' ${basedir}/rsync.conf`src=`grep 'local directory=' ${basedir}/rsync.conf|cut -d '=' -f 2`des=`grep '^dest' ${basedir}/rsync.conf| cut -d '=' -f 2 `

    #inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format  '%T %w%f %e' \ --event modify,create,move,delete  ${src} | while read  date time file event        do                echo $event            for i in $des            do
                    case $event in                MODIFY|CREATE|MOVE|MODIFY,ISDIR|CREATE,ISDIR|MODIFY,ISDIR)                        #echo $src                        no_src_root_file_name=`echo $file|sed "s#$src##g"`                        final_target_dest=$i$no_src_root_file_name
                           echo rsync -avz --delete --progress $file $final_target_dest                        rsync -avz --delete --progress $file $final_target_dest                ;;
                    DELETE|DELETE,ISDIR)                        src_file_up_dir=`echo $file|awk -F"/" '{NF=NF-1;OFS="/";print $0}'`                        no_root_src_file_up_dir=`echo $file|awk -F"/" '{NF=NF-2;OFS="/";print $0}'|sed "s#$src##g"`                        final_target_dest_up_dir=$i$no_root_src_file_up_dir                echo    rsync -avz --delete --progress $src_file_up_dir $final_target_dest_up_dir                        rsync -avz --delete --progress $src_file_up_dir $final_target_dest_up_dir                ;;                esac            done        done
    ############################################################################################
    rsync.conflocal directory=/EBS/www/projects#dest_heredest=root@174.129.219.40:/EBS/www
    20090723:--format '%T %w%f'其中的%f参数:   %f     When an event occurs within a directory, this will be replaced with the name of the  File  which  caused he event to occur.  Otherwise, this will be replaced with an empty string.如果不加%f参数,当一个文件夹里的文件发生变化时,不会输出文件名(需要自己echo脚本里read的变量),而时输出发生变化的文件的文件夹名,可以将这一点用于减少rsync遍历中的delete事件。另外,%w是用于输出发生变化的文件的。       %w     This will be replaced with the name of the Watched file on which an event occurred.
     发表于: 2008-10-13,修改于: 2010-02-22 09:34 已浏览3044次,有评论7条推荐投诉

      网友评论
     cc0cc 时间:2008-12-11 09:51:52 IP地址:202.165.107.★


    请教下,这样实时的同步和cronjob+rsync完成的差距有多大呢?什么场景下需要使用如此高的实时呢(当然用cronjob也可以把时间尽量少到一秒)?

    http://www.54chen.com

    Blog作者的回复:
    如果你对实时性要求不是很高的话完全可以使用crontab,然后把循环时间改成1秒,但是每一秒,不管文件有没有变化,都需要去执行一下rsync,是不是会占用不必要的系统资源呢?


     本站网友 时间:2009-04-17 17:42:28 IP地址:221.122.41.★


    这个实际应用有很多问题,比如在监控目录解压一个包含许多小文件(几万,上百万)的gz包,或者从监控目录mv走一个目录,那么rsync只会报告找不到你移走的目录而无法删除备份机的应该删除的目录。



     本站网友 时间:2009-04-17 18:02:32 IP地址:221.122.41.★


    sorry ,把  rsync -avz --delete --progress ${src} root@${ip}:${des} && 中的 ${src}看成是$file 了, 不过弄成这样,其实和cron没有太大区别,rsync每次 还是要历遍目录,当上百万千万的文件时,这样的方案就是不可用的了 。要充分利用inotify的通知功能,只更新文件而不需要历遍,网上有很多perl写的程序就做到了。

    Blog作者的回复:
    当文件达到一定数量时,这个确实是个问题,有时间再找找解决的办法。


     本站网友 时间:2009-05-20 17:31:29 IP地址:121.8.131.★


    这个星期刚好做了个试验,在一百万个文件级别下,rsync一次根目录需要非常长的时间,在最底层的目录下则几乎是实时的速度。脚本可能需要改写才能好用


     brucejin 时间:2009-07-27 11:43:42 IP地址:122.224.217.★


    inotify如何去区分一个文件完全被创建?因为我发现不管文件多大,在操作系统上都会先创建一个大小为几k的文件。这时已经激活了notifiy事件,同时脚本已经运行。导致同步文件不完整。如何解决?

    Blog作者的回复:
    创建文件时触发的是CREATE事件,这时触发rsync,在你说的没有完全创建文件时,会有后续的MODIFY事件,这个事件也可以触发rsync脚本。另外,如果只是想在创建开始和创建完成时触发时间,你可以监控CLOSE_WRITE这个事件。在创建文件完成时会有这个事件,你可以监控一个文件,详细的看一下创建一个文件的完整过程,来制定自己满意的触发机制。man inotifywait


     本站网友 时间:2009-11-04 15:48:12 IP地址:124.42.5.★


    这种方法能否替代nfs ? 我按你的文章实验了一下,如果我想多台服务器同时读写删除同时通知其他服务器的话,我必须在所有服务器上启动你上面说的脚本,但这样的话会冲突,不知道你现在解决了这个问题没有。

    Blog作者的回复:
    不能使用这种方法替代nfs。


     本站网友 时间:2010-02-21 18:54:18 IP地址:114.255.44.★


    http://code.google.com/p/sersync/

    sersync主要用于服务器同步,web镜像等功能。基于boost1.41.0,inotify api,rsync command.开发。目前使用的比较多的同步解决方案是inotify-tools+rsync ,另外一个是google开源项目Openduckbill(依赖于inotify- tools),这两个都是基于脚本语言编写的。相比较上面两个项目,本项目优点是: 

    sersync是使用c++编写,而且对linux系统文件系统产生的临时文件和重复的文件操作进行过滤(详细见附录,这个过滤脚本程序没有实现),所以在结合rsync同步的时候,节省了运行时耗和网络资源。因此更快。 

    相比较上面两个项目,sersync配置起来很简单,其中bin目录下已经有基本上静态编译的2进制文件,配合bin目录下的xml配置文件直接使用即可。 

    另外本项目相比较其他脚本开源项目,使用多线程进行同步,尤其在同步较大文件时,能够保证多个服务器实时保持同步状态。 

    本项目有出错处理机制,通过失败队列对出错的文件重新同步,如果仍旧失败,则每10个小时对同步失败的文件重新同步。 

    本项目自带crontab功能,只需在xml配置文件中开启,即可按您的要求,隔一段时间整体同步一次。无需再额外配置crontab功能。 

    本项目socket与http插件扩展,满足您二次开发的需要。 


    Blog作者的回复:
    谢谢,好东西,一定要好好学习下