Linux环境下使用inotify与sersync实现rsync实时数据同步
一、 inotify 与 rsync 的基础整合
在数据备份与分发场景中,传统的定时任务(如 Cron)结合 rsync 无法满足毫秒级或秒级的实时性需求。自 Linux 内核 2.6.13 起,系统原生支持 inotify 文件系统监控机制。通过将 inotify 与 rsync 结合,可以构建高效的实时数据同步架构。
目前主流的 inotify 封装工具包括 inotify-tools、sersync 和 lsyncd。本文将首先剖析 inotify-tools 的底层机制与局限性,随后引入企业级解决方案 sersync。
1. 环境准备与 inotify-tools 部署
在使用 inotify 之前,需确认系统内核支持并检查相关内核参数。可以通过 sysctl 命令查看和调整 inotify 的资源限制:
# 查看当前 inotify 内核参数
sysctl fs.inotify.max_queued_events
sysctl fs.inotify.max_user_instances
sysctl fs.inotify.max_user_watches
# 临时调高监控数量上限(适用于海量文件目录)
sysctl -w fs.inotify.max_user_watches=30000000
sysctl -w fs.inotify.max_queued_events=327679
参数说明:
- max_queued_events:inotify 实例的事件队列最大长度。若超出此值,将触发
Q_OVERFLOW事件并丢弃后续事件。 - max_user_instances:单个用户可创建的 inotify 实例总数上限。
- max_user_watches:单个 inotify 实例可监控的文件/目录节点总数。监控大型项目时必须调大此值。
安装 inotify-tools(以源码编译为例):
wget https://github.com/inotify-tools/inotify-tools/archive/refs/tags/3.22.1.0.tar.gz
tar -xf 3.22.1.0.tar.gz
cd inotify-tools-3.22.1.0
./autogen.sh && ./configure --prefix=/opt/inotify
make && make install
ln -sv /opt/inotify/bin/inotifywait /usr/local/bin/
2. inotifywait 核心参数与事件模型
inotifywait 是 inotify-tools 的核心组件,用于阻塞或持续监听文件系统事件。常用参数如下:
-m:持续监控模式,不随单次事件退出。-r:递归监控目录及其所有子目录。-q:静默模式,仅输出必要信息。-e:指定监听的事件类型(如close_write,delete,moved_to)。--exclude:使用正则表达式排除特定文件或目录。--format:自定义输出格式,如'%w%f %e'。
在实际同步场景中,并非所有事件都需要关注。例如,access(读取)和 open(打开)事件对数据同步毫无意义。推荐使用以下事件组合:
close_write:文件写入完成并关闭(核心同步触发点)。delete:文件或目录被删除。moved_to/moved_from:文件移入或移出监控目录。create,isdir:创建新目录(确保目录结构同步)。
3. 架构部署位置说明
在典型的"一主多从"发布架构中(例如 1 台发布机,3 台 Web 节点),inotify 必须部署在数据源端(发布机)。发布机监控本地目录变化,并作为 rsync 客户端主动将变更推送到配置为 rsync daemon 的 Web 节点。这种"推"模式能最大程度保证数据分发的实时性。
二、 inotify+rsync 脚本实现与性能调优
1. 基础同步脚本(存在性能缺陷)
以下是一个常见的入门级监控脚本。该脚本通过管道将 inotifywait 的输出直接传递给 rsync:
#!/bin/bash
SRC_DIR="/data/webroot"
DEST_HOST="192.168.1.100"
RSYNC_MODULE="web_sync"
inotifywait -mrq -e close_write,delete,moved_to,moved_from --format '%w%f' "$SRC_DIR" | while read -r CHANGED_FILE; do
# 每次文件变化都触发全目录同步
rsync -avz --delete "$SRC_DIR/" "rsync_user@${DEST_HOST}::${RSYNC_MODULE}/"
done
缺陷分析:当向监控目录批量拷贝 1000 个文件时,inotifywait 会产生 1000 次事件,导致 rsync 被连续调用 1000 次。虽然 rsync 具备增量比对机制,但频繁的进程创建、网络握手和目录遍历会消耗大量 CPU 和 I/O 资源,甚至导致同步延迟。
2. inotify 的已知缺陷与竞态条件
在使用 inotifywait -r 递归监控时,如果目标目录结构较深且包含大量文件,在目录创建的瞬间,底层 C 代码存在竞态条件(Race Condition),会导致部分 close_write 事件随机丢失。这意味着如果脚本仅针对"发生变化的单个文件"进行精准同步,极易造成数据漏传。
此外,使用 Vim 等编辑器修改文件时,会产生 .swp、.swx 及 ~ 后缀的临时文件。如果不加过滤,这些临时文件会被同步到生产环境,可能引发不可预知的故障。
3. 优化版批量同步脚本
为了解决频繁调用 rsync 的问题,我们可以引入"日志缓冲与批量处理"机制。将事件写入日志文件,通过轮询日志文件的状态来触发 rsync,从而将多次事件合并为一次或两次同步操作:
#!/bin/bash
MONITOR_PATH="/data/webroot"
REMOTE_IP="192.168.1.100"
MODULE_NAME="web_sync"
EVENT_LOG="/var/log/inotify_batch.log"
# 1. 初始全量同步,确保基线一致
rsync -avz --delete --exclude="*.swp" --exclude="*.swx" "$MONITOR_PATH/" "rsync_user@${REMOTE_IP}::${MODULE_NAME}/"
# 2. 后台持续监控并记录事件(排除临时文件)
inotifywait -mrq -e close_write,delete,moved_to,moved_from,create \
--exclude '\.swp$|\.swx$|.*~' \
--format '%w%f' "$MONITOR_PATH" > "$EVENT_LOG" &
# 3. 轮询日志文件,触发批量同步
while true; do
if [ -s "$EVENT_LOG" ]; then
# 执行目录级同步
rsync -avz --delete --exclude="*.swp" --exclude="*.swx" "$MONITOR_PATH/" "rsync_user@${REMOTE_IP}::${MODULE_NAME}/"
# 清空日志,防止下一轮重复同步
truncate -s 0 "$EVENT_LOG"
# 补偿同步:防止在清空日志瞬间产生的新事件被遗漏
rsync -avz --delete --exclude="*.swp" --exclude="*.swx" "$MONITOR_PATH/" "rsync_user@${REMOTE_IP}::${MODULE_NAME}/"
else
# 无事件时休眠,降低 CPU 占用
sleep 2
fi
done
此脚本通过 truncate 清空日志并辅以二次补偿同步,在保证数据完整性的前提下,将 rsync 的调用频率降低了数个数量级。
三、 sersync:企业级实时同步引擎
尽管优化后的 Shell 脚本能缓解性能问题,但在处理 TB 级数据、高并发写入以及失败重试等复杂场景时,仍显力不从心。sersync 是一款基于 C++ 编写的专业级 inotify 封装工具,专为 rsync 实时同步设计。
1. sersync 的核心优势
- 事件过滤与去重:自动过滤临时文件,合并短时间内的重复事件,大幅减少 rsync 调用。
- 多线程并发:支持多线程调用 rsync,显著提升海量小文件的同步吞吐量。
- 失败重试机制:内置错误队列,对同步失败的文件进行定时重传。
- 内置定时全量比对:无需依赖系统 crontab,即可配置周期性的整体目录校验。
2. 部署与配置
sersync 为绿色免安装软件,解压即可使用。建议将其放置于 /opt/sersync 目录:
wget https://github.com/wsgzao/sersync/raw/master/sersync2.5.4_64bit_binary_stable_final.tar.gz
tar -xf sersync2.5.4_64bit_binary_stable_final.tar.gz
mv GNU-Linux-x86 /opt/sersync
ln -sv /opt/sersync/sersync2 /usr/local/bin/
sersync 的核心行为由 confxml.xml 文件控制。以下是一份经过优化的生产环境配置示例:
<?xml version="1.0" encoding="ISO-8859-1"?>
<head version="2.5">
<host hostip="localhost" port="8008"></host>
<debug start="false"/>
<fileSystem xfs="false"/>
<!-- 过滤规则:排除不需要监控的文件或目录 -->
<filter start="true">
<exclude expression="(.*)\.svn"></exclude>
<exclude expression="(.*)\.git"></exclude>
<exclude expression="^logs/*"></exclude>
<exclude expression="(.*)\.swp"></exclude>
</filter>
<!-- 监控事件类型配置 -->
<inotify>
<delete start="true"/>
<createFolder start="true"/>
<createFile start="false"/>
<closeWrite start="true"/>
<moveFrom start="true"/>
<moveTo start="true"/>
<attrib start="false"/>
<modify start="false"/>
</inotify>
<sersync>
<!-- 本地监控路径与远程目标配置 -->
<localpath watch="/data/webroot">
<remote ip="192.168.1.100" name="web_sync"/>
</localpath>
<rsync>
<commonParams params="-avz --delete"/>
<auth start="true" users="rsync_user" passwordfile="/etc/rsyncd.secret"/>
<userDefinedPort start="false" port="873"/>
<timeout start="true" time="300"/>
<ssh start="false"/>
</rsync>
<!-- 失败重试机制:每 60 分钟执行一次失败重传 -->
<failLog path="/opt/sersync/rsync_fail_log.sh" timeToExecute="60"/>
<!-- 定时全量同步:每 12 小时进行一次整体目录比对 -->
<crontab start="true" schedule="720">
<crontabfilter start="true">
<exclude expression="*.tmp"></exclude>
<exclude expression="logs/*"></exclude>
</crontabfilter>
</crontab>
</sersync>
</head>
3. 启动与多实例管理
配置完成后,使用 sersync2 命令启动服务。常用参数包括:
-d:以守护进程模式在后台运行。-r:在启动监控前,先执行一次全量 rsync 推送,确保两端数据基线一致。-o:指定自定义的 XML 配置文件路径(用于多实例场景)。-n:指定并发线程数(默认 10,可根据 CPU 核心数调整)。
启动单实例服务:
sersync2 -r -d -o /opt/sersync/confxml.xml
若需同时监控多个不同的业务目录(如 Nginx 配置目录和 Web 代码目录),只需复制并修改 XML 配置文件,然后分别启动即可:
# 实例 1:同步 Web 代码
sersync2 -r -d -o /opt/sersync/conf_web.xml
# 实例 2:同步 Nginx 配置
sersync2 -r -d -o /opt/sersync/conf_nginx.xml
通过 sersync 的多实例与多线程特性,可以在复杂的微服务或集群架构中,轻松实现低延迟、高可靠的数据实时分发。