了解rsync的优缺点之后,就需要了解Rsync结合inotify实现数据如何实时同步,以下案例可以供童鞋们一起学习下
## 七 Rsync结合inotify实现数据实时同步
### 7.1 rsync+inotify介绍
rsync+crond:只能实现数据的间隔同步(最小也是分钟级别的同步),无法实现实时同步,如果要做异地镜像点,肯定是需要实时同步的。
在Linux kernel 2.6.13后提供了inotify文件系统监控机制。通过rsync+inotify组合可以实现实时同步,制作异地镜像站点,以便为异地备份做好准备工作
### 7.2 安装inotify-tools
inotify由inotify-tools包提供。在安装inotify-tools之前,请确保内核版本高于2.6.13,且在/proc/sys/fs/inotify目录下有以下三项,这表示系统支持inotify监控,关于这3项的意义,下文会简单解释。
```
[root@local ~]# 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源码包地址:
以下为编译安装过程:
```
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@local ~]# rpm -ql inotify-tools | grep bin/
/usr/bin/inotifywait
/usr/bin/inotifywatch
```
其中inotifywait命令用于等待文件发生变化,所以可以可以实现监控(watch)的功能,该命令是inotify的核心命令。inotifywatch用于收集文件系统的统计数据,例如发生了多少次inotify事件,某文件被访问了多少次等等,一般用不上。
以下是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实例可监控的最大目录、文件数量。如果监控的文件数目巨大,需要根据情况适当增加此值。
如:
```bash
[root@xuexi ~]# echo 30000000 > /proc/sys/fs/inotify/max_user_watches
```
### 7.3 inotifywait命令与事件
#### 7.3.1 inotifywait命令
| 选项 | 说明 |
| :------: | :----------------------------------------------: |
| -m | 持续监听 |
| -r | 使用递归形式监视目录 |
| -q | 减少冗余信息,只打印出需要的信息 |
| -e | 指定要监视的事件,多个时间使用逗号隔开 |
| –timefmt | 时间格式 |
| –format | 监听到的文件变化的信息,信息格式自定义,详解下表 |
–format 参数说明:
| 参数 | 说明 |
| :--: | :----------------------------------------------------------: |
| %w | 表示发生事件的目录 |
| %f | 表示发生事件的文件,%w%f组在一起即表发生事件的绝对路径 |
| %e | 表示发生的事件,事件默认是以逗号为分隔符的,例如CLOSE_WRITE,CLOSE |
| %Xe | 事件以“X”分隔,例如CLOSE_WRITEXCLOSE,代表事件CLOSE_WRIT与CLOSE |
| %T | 使用由--timefmt定义的时间格式,此时必须指定--timefmt参数 |
-e指定的监控事件
```bash
create:在被监控的目录中创建了文件或目录
delete:删除了被监控目录中的某文件或目录
modify:修改,文件内容被修改
access:文件被访问
attrib:元数据被修改。包括权限、时间戳、扩展属性等等
close_write:打开的文件被关闭,是为了写文件而打开文件、随后被关闭的事件,比如用vim编辑文件或者echo 1111 >> egon.txt
close_nowrite:read only模式下文件被关闭,即只能是为了读取而打开文件,读取结束后关闭文件的事件
close:是close_write和close_nowrite的结合,无论是何种方式打开文件,只要关闭都属于该事件
open:文件被打开
moved_to:向监控目录下移入了文件或目录,也可以是监控目录内部的移动
moved_from:将监控目录下文件或目录移动到其他地方,也可以是在监控目录内部的移动
move:是moved_to和moved_from的结合
moved_self:被监控的文件或目录发生了移动,移动结束后将不再监控此文件或目录
delete_self:被监控的文件或目录被删除,删除之后不再监控此文件或目录
umount:挂载在被监控目录上的文件系统被umount,umount后不再监控此目录
isdir :监控目录相关操作
```
#### 7.3.2 事件分析
监控目录
```bash
[root@egon ~]# mkdir /egon_bak
[root@egon ~]# inotifywait -m /egon_bak/ #在前台监控目录/egon_bak,未指定监控的事件,默认监控所有
Setting up watches.
Watches established.
```
打开另外一个终端,对被监控目录/egon_bak进行一些操作,来查看触发的事件
```bash
验证文件的增删改查,vim编辑文件,cp拷贝,mv移动文件等操作触发的事件
验证目录的增删改查,cp拷贝文件到目录下,mv移入移出等操作触发的事件
```
大致总结如下
```bash
# 文件的增删改
监控目录下新增了某个文件->触发的事件:
CREATE
MODIFY
CLOSE_WRITE,CLOSE
监控目录下的某个文件被删除->触发的事件
DELETE
监控目录下的某个文件内容被修改->触发的事件
MODIFY
CLOSE_WRITE,CLOSE
监控目录下的某个文件属性被修改->触发的事件
ATTRIB
监控目录下的某个文件被mv到别的目录中->出发的事件
MOVED_FROM
某个文件被mv到了监控目录或其子目录下->出发的事件
MOVED_TO
# 目录的增删改同上
CREATEXISDIR
DELETEXISDIR
ATTRIBXISDIR
MOVED_FROMXISDIR
MOVED_TOXISDIR
```
从上面的测试结果中可以发现,很多动作都涉及了close事件,且大多数情况都是伴随着close_write事件的。所以,大多数情况下在定义监控事件时,其实并不真的需要监控open、modify、close事件。特别是close,只需监控它的分支事件close_write和close_nowrite即可。由于一般情况下inotify都是为了监控文件的增删改,不会监控它的访问,所以一般只需监控close_write即可。
由于很多时候定义触发事件后的操作都是根据文件来判断的,例如a文件被监控到了变化(不管是什么变化),就立即执行操作A,又由于对文件的一个操作行为往往会触发多个事件,例如cat查看文件就触发了open、access、close_nowrite和close事件,这样很可能会因为多个事件被触发而重复执行操作A。
综合以上考虑,我们应该尽量做到监控对象减少重复,但这一点其实很难实现
```bash
inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%Xe:%T' -e create,delete,modify,move,attrib,close_write /test
```
### 7.3.3 rsync+inotify脚本
该脚本需要运行在本地 ,并且需要远端开启rsyncd
```bash
#!/bin/bash
watch_dir=/test/ # 本地被监控目录
user="egon" # 虚拟用户
export RSYNC_PASSWORD=123 # 虚拟用户密码
module="xxx" # 远程模块名
ip=192.168.12.39 # 远程主机ip
# 先整体同步一次
rsync -azc --delete ${watch_dir} ${user}@${ip}::${module}
# 切换到被监控目录下,然后用inotifywait监控./目录,这样后期就可以用-R选项同步新增的子目录
cd $watch_dir
/usr/bin/inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%Xe:%T' -e create,delete,modify,move,attrib,close_write ./ \
--exclude=".*.swp" | \
while read line
do
# $line的输出format为:文件路径:事件:时间
FILE=$(echo $line | awk -F: '{print $1}') # 获取文件的绝对路径
EVENT=$(echo $line | awk -F: '{print $2}') # 获取监控的事件
# 监控到对文件的下述行为后,只把文件同步到远端
if [[ $EVENT =~ 'CREATE' ]] || [[ $EVENT =~ 'MODIFY' ]] || [[ $EVENT =~ 'CLOSE_WRITE' ]] || [[ $EVENT =~ 'MOVED_TO' ]] || [[ $EVENT =~ 'ATTRIB' ]];then
rsync -azcR ${FILE} ${user}@${ip}::${module}
fi
# 监控到涉及到目录的改动,将目录同步到远端,例如用dirname ${FILE}获取目录
if [[ $EVENT =~ 'DELETE' ]] || [[ $EVENT =~ 'MOVED_FROM' ]];then
rsync -azcR --delete $(dirname ${FILE}) ${user}@${ip}::${module} &>/dev/null
fi
done &
# 末尾的&符号,代表在子shell中提交命令,这样进程的ppid就变为1,当前窗口关闭,该进程依然存活
```
远程(配置好rsync守护进程,模块为xxx,虚拟用户名egon,密码123),执行下述命令检测同步情况
```bash
while true;do ls /egon_bak;sleep 0.5;clear;done
```