柴少的官方网站 技术在学习中进步,水平在分享中升华

inotify+rsync实时同步慢的问题

     当然现在基本各种分布式的情况下,用到inotify+rsync进行目录的实时同步的形式场景已经不是很多了,但是还是有点的,比如机器规模少就两台机器间进行数据同步,可能就要简单的来搞一搞。

这里就不说rsyncd服务器怎么搭建了,以前有一篇记录。

      Inotify 是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。学习如何将 inotify 集成到您的应用程序中,并发现一组可用来进一步自动化系统治理的命令行工具。

      rsync+inotify的简单部署网上一搜一大把,这里就不说了。主要是用inotify来监控目录的变化,一旦变化就通知rsync脚本去同步目录到对端。

#!/bin/sh
src=/data/
des=/opt/data
ip=xxx.xxx.xxx.xxx
user=admin
/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 --password-file=/etc/rsyncd.pass ${src} ${user}@${ip}::${des} &&
 done

#上面的脚本是网上流传的比较简单的一个同步脚本,当然avz也实现了增量备份,--delete参数也保证了源端会将目标端目录里面不一致的内容删除掉,保证两端内容一致。

#上面这个脚本同步文件其实是问题不太大了,当然文件数量少你也感觉不出来有延时啊,同步慢啊之类的问题。

#当你拷贝了一个目录到这个同步目录的时候,如果目录里面有几百个文件,你会发现同步不实时了,文件一点点的同步过去,这是为什么呢,你如果不理解这个脚本的意思,可以记录下日志,查看一下日志是怎么输出的,你就会发现,一个文件就会造成两次的rsync同步进程,那么你拷贝一个目录进来,目录里面有几百个文件,好吧inotifywait一监听到变化就会通知rsync去同步,那么rsync同步就会等待上一个执行完再起一个,然后你观察进程就会发现rsync进程起了一个又一个,但是目录里的文件很慢的才会同步完成。

#inotify的监听原理以及其他人是怎么解决这个问题的可以上网搜,这里我就是把问题简单化一点简单的解决了下。

我就反着来了,应该是先写同步脚本,再写监控的。

博文来自:www.51niux.com

第一步:编写定时任务:

#crontab  -l

#####webdir themes sync monitor######
*/10 * * * * /bin/bash /opt/scripts/monitor_sync.sh >/dev/null 2>&1    #这是一个监控同步的定时任务,如果发现同步程序有异常就会重新把同步进程拉起一次,每10分钟执行一次,当然调小点也没关系
0 * * * * /bin/bash /opt/scripts/Web_hour_sync.sh >/dev/null 2>&1      #这里为了保证万无一失,每小时还会进行增量同步一次

#cat /opt/scripts/monitor_sync.sh

#!/bin/bash
mdir=/opt/scripts/monitor_dir/themes
rsynclog=/var/log/rsync.log
shdir=/opt/scripts
if [ `ps -ef|grep inotifywait|grep -v  grep|wc -l` -ne 4 ];then     #我这里有四个目录需要inotifywait监听
   cd ${shdir}
   /bin/bash  ${shdir}/monitor_stop.sh
   sleep 1
   cd ${shdir}/dirnohup
   /usr/bin/nohup /bin/bash  ${shdir}/monitor_start.sh &
   echo "###############`date +%F" "%H:%M:%S`########[monitor_inotify_restart]rsync######">>$rsynclog
   /bin/bash ${shdir}/Web_hour_sync.sh
fi   #上面的意思是,如果inotifywait进程有问题,就调用下停止脚本,然后再调用下监控启动脚本,然后记录下日志,然后执行下同步,因为重启进程的过程中也可能出现了目录文件的变化
if [ `ps -ef|grep sh|grep monitor|grep bash|grep -v monitor_time.sh|grep -v monitor_sync|wc -l` -lt 8 ];then   #这里就是如果发现监控目录变化的脚本有问题,也重启一次
   cd ${shdir}
   /bin/bash  ${shdir}/monitor_stop.sh
   sleep 1
   cd ${shdir}/dirnohup
   /usr/bin/nohup /bin/bash  ${shdir}/monitor_start.sh &
   echo "###############`date +%F" "%H:%M:%S`########[monitor_sh_restart]rsync######">>$rsynclog
   /bin/bash ${shdir}/Web_hour_sync.sh
fi

if [ `ps -ef|grep monitor_time.sh |grep -v grep|wc -l` -eq 0  ];then   #如果监控时间文件的脚本出问题了,也重新拉起一次,然后再同步一次
   cd ${shdir}/timenohup
   /usr/bin/nohup  /bin/bash ${shdir}/monitor_time.sh &
   echo "###############`date +%F" "%H:%M:%S`#####[monitor_time_restart]rsync######">>$rsynclog
   /bin/bash ${shdir}/Web_hour_sync.sh
fi

#cat /opt/scripts/Web_hour_sync.sh

#!/bin/bash
rsynclog=/var/log/rsync.log
shdir=/opt/scripts/web_rsync
sleep 10
if [ `ps -ef|grep monitor_sync.sh|grep -v grep|wc -l`  -gt 0 ];then #上面哪个脚本如果执行了调用进程重启的话,会一直存在进程中,因为调用的脚本里面有while语句,while不停止,这个脚本就会一直存在于进程,但是它的使命已经结束了,如果存在就杀掉。
   for num in `ps -ef|grep monitor_sync.sh|grep -v grep|awk {'print $2'}`;do kill -9 $num;done
fi
echo "###############`date +%F" "%H:%M:%S`########[Hour]rsync######">>$rsynclog
for n in `ls -l ${shdir}|awk {'print $NF'}|grep "sh"$`    #因为我这里并不是监控的一个目录,所以针对每个目录都写了监控同步脚本,所以是调用好几个同步脚本。
    do
       echo "####[$n] rsync####">>$rsynclog
       /bin/bash ${shdir}/$n >>$rsynclog
    done

#cat /opt/scripts/monitor_start.sh   #这个启动脚本就简单了,就是进入到指定目录下,将所有脚本启动一下。

#!/bin/bash
rsynclog=/var/log/rsync.log
shdir=/opt/scripts/monitor_rsync
for n in `ls -l ${shdir}|awk {'print $NF'}|grep "sh"$`
    do
       /bin/bash $shdir/$n &
       sleep 1
    done

#cat /opt/scripts/monitor_stop.sh   #这个脚本呢就是将所有的相关进程都杀死

#!/bin/bash
rsynclog=/var/log/rsync.log
shdir=/opt/scripts/monitor_rsync
for num in `ls -l ${shdir}|awk {'print $NF'}|grep "sh"$`
    do 
      for pidnum in `ps -ef|grep $num|grep -v grep|awk {'print $2'}`
          do
              /bin/kill -9 $pidnum
          done
    done
for n in `ps -ef|grep inotifywait|grep -v grep|awk {'print $2'}`
    do
       /bin/kill -9 $n
    done

博文来自:www.51niux.com

第二步:监控目录是否发生变化的脚本:

#cat /opt/scripts/monitor_rsync/monitor_html.sh   #这是其中一个脚本,监控目录是否发生变化的脚本都在/opt/scripts/monitor_rsync目录下面,其他的脚本也跟这个类似了,如monitor_blog.sh

#!/bin/bash
LANG=en
src=/usr/local/nginx/html/www.51niux.com/html/
mdir=/opt/scripts/monitor_dir/html
cd /opt/
/usr/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format  '%T %w%f' \
-e modify,delete,create,attrib ${src} \
| while read  file 
  do
    echo "`date +%s`" >$mdir/Last_time.txt   #其实这个脚本里面其他地方还是跟网上的意思一致的,就是监听目录的各种改变嘛,但是当检测到变化的时候我并不是调用rsync而是记录一个时间戳到一个时间目录下的文件
  done

#cat /opt/scripts/monitor_time.sh  #然后会有一个检测时间戳文件里面的内容是否变化的脚本,这个就是关键了,没有这个脚本上面记录时间戳就没有意义了。

#!/bin/bash
rsynclog=/var/log/rsync.log 
Monitor_dir=/opt/scripts/monitor_dir
Sync_dir=/opt/scripts/web_rsync
while true
do
  sleep 1
  for num in `ls -l /opt/scripts/monitor_dir|awk {'print $NF'}|grep [a-z]`     #因为monitor_dir目录下面以每个要同步的目录为名创建了目录,然后此目录下面记录了时间戳文本
     do
      Last_time=`cat $Monitor_dir/$num/Last_time.txt`                     #这个是现在的时间戳文本
      This_time=`cat $Monitor_dir/$num/This_time.txt`                     #这个是同步完文件之后的时间戳文本
      if [ $This_time -le $Last_time ] && [ `ps -ef|grep rsync|grep "${num}/"|wc -l` -eq 0 ];then #如果同步完文件之后的时间戳文本里面的时间戳小于现在的时间戳文本里面的时间戳,就说明文件又要新的变化了
       # [ `ps -ef|grep rsync|grep "${num}/"|wc -l` -eq 0 ]  #如果不加这个判断,发送小文件是没问题的因为很快就发过去了,但是如果是上G的大文件,带宽又比较小,就会造成旧的还没发送完新的rsync又起,这样避免重复的rsync
      sleep 1                                                             #这里加不加都没事,我这里加了是为了保证万一目录一秒复制不完呢等一等多同步点数据过去
      echo "###############`date +%F" "%H:%M:%S`########[${num}]rsync######">>$rsynclog    
      /bin/bash $Sync_dir/Web_${num}_sync.sh >>$rsynclog                  #因为我们是走的一个for循环嘛,所以哪个目录发生了变动,就调用了对应的目录同步脚本
      fi
     done 
done

#cat /opt/scripts/web_rsync/Web_html_sync.sh   #加入html目录发生了改变,就会调用这个同步脚本,剩下的脚本也是依葫芦画瓢,照着这种格式,如Web_blog_rsync.sh

#!/bin/bash
src=/usr/local/nginx/html/www.51niux.com/html/
des=admin@192.168.1.101::Html
mdir=/opt/scripts/monitor_dir/html  #注意时间戳文本插入的目录一定要是,如Web_html_sync.sh,那么时间戳插入的目录就是monitor_dir目录下面的html目录
if [ `ps -ef|grep rsync|grep 'html/'|wc -l` -eq 0 ];then    #因为还会被每小时执行一次同步操作,如果这个时候已经在跑着此目录的rsync,那么这里不判断再启动的话,就会有两个目录的rsync,如果是大文件的话可能会发送好长时间都发送不过去
    echo "`date +%s`" >$mdir/This_time.txt    #一定要将开始同步的时间写在同步进程之前(因为你同步可能会耗时很长,这时候你记录的时间就会偏后但是万一同步期间又有了文件改变呢,时间戳一比较,就会出岔子,导致漏点文件同步)
    cd /opt && /usr/bin/rsync -avz --delete --progress $src $des --password-file=/etc/rsyncd.passwd
fi

#ls -l /opt/scripts/monitor_dir/html/   #查看一个记录时间戳的目录

图片.png

#cat /opt/scripts/monitor_dir/html/Last_time.txt    #如果这里记录的时间比下面记录的时间大,就会调用目录rsync同步脚本

1501671159

#cat /opt/scripts/monitor_dir/html/This_time.txt  #如果这里记录的时间比上面记录的时间大,就不会调用目录rsync同步脚本

1506754811

梳理一下:

  1. 这些脚本就类似于构成了一个小程序,有检测进程的出问题就重启一下,有检测目录变化的,有进程目录同步的。

  2. 这个虽然提高了同步的效率,但是并不是实时同步了,如果要做到觉得实时的同步,这个小例子就不适合了。它最低可以实时到延迟1-2秒。

  3. 如果只是单个目录的实时监测并同步的话,并不需要这么复杂,我这个例子是适应监测多目录的变化并同步。

  4. 因为多次用到了for循环并调用对应的脚本,所以在目录的创建和脚本的命令上面还是比较讲究的,要按照统一的格式来,上面已经给出了例子。

  5. 这是以前写的,现在看来也并不比网上写的一个大脚本简单,如果觉得写的太垃圾了的话,可以忽略......

作者:忙碌的柴少 分类:解决小问题 浏览:4841 评论:0
留言列表
发表评论
来宾的头像