印度机房用 inotify 等文件事件,等来的却是丢失的 tail

上个月在孟买机房的 Ubuntu 20.04 那台日志中转机上,有个目录要实时收上游推来的日志分片,一天大概 15 万个小文件、30GB 左右。我第一版用 inotifywait -m -r -e create,moved_to 监听,脚本里拿到文件名就 tail -f 读几行入库。跑了两天发现漏文件,高峰期能丢 3% 左右,日志里查不到任何报错。

把监听改成同时记事件和 /proc/sys/fs/inotify/max_queued_events,才看到队列溢出。默认值 16384,上游是一次性 rsync 推几百个分片,事件在极短时间内灌满队列,内核直接丢事件,用户态完全无感。

我的做法是两层兜底。第一层放大内核参数并加监控:

sysctl -w fs.inotify.max_queued_events=1048576
sysctl -w fs.inotify.max_user_watches=1048576
sysctl -w fs.inotify.max_user_instances=512

第二层不再只信事件。inotify 只用来触发「有事发生了」,真正扫描用 mtime 水位线,每 30 秒 find 一次比上次水位新的文件,按文件名去重:

find /data/in -type f -newermt "@$last_ts" -printf '%T@ %p\n' \
  | sort -n | while read ts f; do last_ts=$ts; enqueue "$f"; done

为什么这样:inotify 是内核事件队列,不是可靠消息队列,溢出即丢,且不通知。它适合做低延迟触发器,不适合做唯一的事实来源。代价是每 30 秒一次 find,15 万文件的目录在机械盘上大概 1.5 秒,CPU 能接受;如果目录到百万级,就得换成按天分目录,否则扫描本身会成为瓶颈。

哪里会失效:容器里 max_user_instances 是宿主机级别,改容器内 sysctl 常常被拦;NFS 挂载上 inotify 根本不触发远端写入事件,我一开始就把上游目录挂成了 NFS,白等了一晚;另外 -newermt 依赖 mtime,上游如果 rsync -t 保留旧时间戳,水位线会永远追不上,得改用文件名里的时间或哈希索引。

你们在印度这种跨区推送的场景,是直接信 inotify,还是也留了一条定时扫描的兜底?

0
赞同度 100% · 0 条评论 · 2 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。