一般来说,从文件系统中获得文件变化信息,调用操作系统提供的 API 即可。Windows 操作系统上有个名为 ReadDirectoryChangesW 的 API 接口,只要监视一个目录路径就可以获得包括其子目录下的所有文件变化信息,简单高效;接口的支持度也很广,现有主流的 Windows 操作系统都支持,往前还可以追溯到 Windows 2000。对码农来说,能提供稳定有效且好用的 API 的系统就是好系统。而本文将讨论 iGuard 网页防篡改系统在 Linux 上获取文件变化信息的方法及从 NFS 网络文件系统中获取文件变化时遇到的困难和心得。

▲ReadDirectoryChangesW

在 Linux 系统上获取文件变更信息,就没有这样的好运了,想要一个像 Windows 上一样提供 ReadDirectoryChangesW 功能的 API,似乎是一种奢望。Linux 内核版本 2.4.0 (2001) 中引入了一个叫 dnotify 的目录检测机制,不怎么好用;内核 2.6.13 (2005) 引入了新方法 inotfiy,但它与 ReadDirectoryChangesW 相比还是差一大截,一次调用只能监视当前目录下的文件变更,子目录里的变更则是无法感知的。如果要获取整个目录下的所有文件变化,应用程序需要遍历整个目录,并把所有的目录监视起来。通过 inotify 接口获得一个目录创建事件时,需要把这个新建的目录及时添加到监视列表,才有可能获得新目录下的文件变化。应用程序处理变化信息较慢时,在把新建目录添加到监视列表前,新目录下的文件事件是极有可能丢失的。对于一个巨型文件系统来说,遍历出所有的目录也是件费事耗资源的任务。如此看来,inotify 机制并不完善,小规模文件数量的场景尚能胜任,像集约化平台,文件规模太庞大,就难以满足了。

▲dnotify

▲inotify

随着 Linux 系统的演化,获取文件变化信息的手段也在发展,但一直不完善,inotify 的缺陷同样表现在 NFS 系统上。NFS 系统是天存信息的用户中一种常见的应用场景,它共享海量文件。我们的 iGuard 网页防篡改系统在 NFS 上需要一种可靠的获取文件变化的手段,来保障安全业务的 7×24h 运转。鉴于 Linux 系统公开的 API 似乎不能满足我们的要求,只有另辟蹊径。幸好 Linux 是开源的,没有现成的就改一个出来。

我们的改造目标指向了 NFS 系统的服务模块 nfsd。在 Linux 内核源代码树下的文件系统 fs 目录中很容易找到 nfsd 模块的同名目录。在 Linux 系统中,NFS 服务透过虚拟文件系统 VFS 接口来访问真实的文件系统,文件的新建、改写、改名和删除等动作是非常清晰的。我们很快就把这些文件更改相关的事件传递出来并为我所用。

最初,我们的这种 nfsd 解决方案和 iGuard 网页防篡改系统看起来是可以一起工作的,在用户生产环境下可以稳定地跑上几周几个月,基本上没有问题。随着集约化平台的兴起,大量网站集中到统一的管理平台下进行内容编辑和运维,这样的单一管理平台发布文件的规模每天可达百万级别。我们的 iGuard 系统在超大规模的文件发布量下也暴露出一些问题,文件同步任务阻塞、滞后或者遗漏等;这些问题以前可能没有出现或缺少关注,随着规模变大,这些问题现今被放大了。这些问题中,最难排解的就是文件遗漏,明明磁盘文件已经更新,但系统就是没有把文件内容同步到远端。后来追查发现,在某些情况下,我们无法获得 NFS 服务所写文件对象的完整文件路径,进而无法输出对应文件的变更消息。

在 Linux 文件系统中,inodedentry 是两个重要的数据结构 。前者对应于磁盘文件的元数据 (类型、尺寸、权限等,但不包括文件路径) 和文件数据块索引,每个 inode 都有一个编号,在文件系统中是唯一的;后者是文件系统运行过程中创建的内存对象,组合成目录项高速缓存 dcache,每个 dentry 对应文件路径上的一个节点并和一个 inode 相关联,目录树由这些 dentry 组成,可以通过遍历目录树来获取文件路径,dentry 可以被视作某种缓存信息,让文件系统运行得更快更高效。

通过 NFS 对外提供文件访问的系统需要符合文件系统可导出规范,这个规范在 Linux 的内核文档 Making Filesystems Exportable 中有简要说明,其中提到 NFS 通信协议中使用文件句柄来标识文件,而不是平时所想象的按文件路径来定位文件。这个句柄信息跟符合可导出规范的文件系统相关,包含 inode 的编号、文件系统标识等信息。这里可以看出,我们需要的文件变化消息是基于文件路径,而 NFS 操作文件是基于这种文件句柄,这里就存在从文件句柄到文件路径的转译过程。

在一般情况下,这个转译过程是正常的,每一个 NFS 文件句柄都可以在 dcache 中找到对应的文件。文档 Making Filesystems Exportable 中还提到 dcache 构建中的 2 个注意事项,大致是:

  • dcache 包含的对象有时候是没有合适前缀的节点 (可以理解为孤立的),该节点没有与根节点相连。
  • 新遍历出来的节点可能是已存在于 dcache 的孤立节点,这种情况需要将孤立节点移动到合适的位置 (可以理解为孤立节点回归到大目录树下)。

这就解释了我们在 NFS 系统中遇到的问题原因——无法获取变更文件的完整路径,因为它没有和根节点相连。我们也能重现问题,在 NFS 服务和客户端工作了一段时间后,重启 NFS 服务器,当 NFS 客户端继续读写曾经访问过的文件时,由于 NFS 服务器上的 dcache 已经复位,客户端请求过来的文件句柄是合法的,并在服务器端形成一个没有合适前缀的节点,这样的节点是无法解析出完整路径的。dcache 毕竟是一个缓存系统,不可能把磁盘上的目录树全部保存到内存,当内存不够用时,dcache 会释放一部分数据并进行内存回收。

NFS 服务的这个问题看似无解,是 NFS 工作模式引发的。为了解决问题,我们尝试采用一种看似笨拙但有效的方法,创造性地构建一张持久化的超大表来跟踪所有的 NFS 文件句柄,记录它们的文件路径信息,通过这张大表转译出那些没头脑的节点。用磁盘空间来换取 NFS 文件句柄的路径检索。方法虽不完美,但我们尽力让 iGauard 网页防篡改系统运行更加完美。(徐品华 | 天存信息)

Ref

  1. Filesystem notification series by Michael Kerrisk
  2. Making Filesystems Exportable

iGuard和NFS文件同步的解决方案的更多相关文章

  1. php中并发读写文件冲突的解决方案(文件锁应用示例)

    PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适 ...

  2. Sersync实现触发式文件同步 替代inotify和rsync

    Sersync实现触发式文件同步 替代inotify和rsync Pyinotify是一个Python模块,用来监测文件系统的变化. Pyinotify依赖于Linux内核的功能—inotify(内核 ...

  3. 挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务

    挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务#36氪开放日# 其他 JasonZheng • 2012-04-07 15:14 来自36氪开放 ...

  4. Seafile 推出 “分布式文件同步技术” 打造的私有云服务

    近两年来 Dropbox 等云储存服务迅速窜红,各大巨头纷纷推出自家的云储存服务(苹果的 iCloud, 微软的 SkyDrive, Google 即将推出的 GDrive),国内也有类似的服务(金山 ...

  5. rsync+sersync+inotify实现服务器间文件同步之一

    rsync+sersync+inotify实现服务器间文件同步之一:rsync安装配置 2013年12月14日 ⁄ Linux管理, 服务器集群技术 ⁄ 共 4925字 ⁄ rsync+sersync ...

  6. 负载均衡配置下的不同服务器【Linux】文件同步问题

    负载均衡配置下的不同服务器[Linux]文件同步问题2017年04月13日 22:04:28 守望dfdfdf 阅读数:2468 标签: linux负载均衡服务器 更多个人分类: 工作 问题编辑版权声 ...

  7. 公司和家里代码文件同步方案: (git和dropbox实现)

    公司和家里代码文件同步方案: (git和dropbox实现) 参与公司福利购入了有补贴的macbook pro后, 就不用上下班背着电脑了. 但是也出现了另外一问题: 家里和公司代码同步的问题 公司有 ...

  8. java大文件上传解决方案

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  9. rsync nfs 实时同步,结合实战

    目录 rsync nfs 实时同步,实战 一.部署rsync服务端(backup) 二.部署rsync客户端(nfs,web01) 三.部署web代码(web01) 四.NFS服务端部署(nfs) 五 ...

随机推荐

  1. 第二十五篇 -- QTreeWidget右击菜单的实现

    效果图: 自己画一个ui,如图,前面已经讲过怎么用Qt Designer画图了,现在就不赘述了. ui_play.py # -*- coding: utf-8 -*- # Form implement ...

  2. vue源码阅读笔记

    1.yarn test [文件名]  -t [name-of-spec(describe or test )] 直接运行yarn test,会测试所有测试文件:yarn test 后面只跟文件名的话会 ...

  3. js学习笔记之日期倒计时DOM操作

    1.访问html元素 getElementById() 方法  返回对拥有指定 id 的第一个对象的引用,只有dom对象有效 getElementsByName() 方法  返回指定名称的对象集合 g ...

  4. 【Java经验分享篇01】小白如何开始学会看开源项目?

    目录 前言 1.理解开源 1.1.什么是开源? 1.2.开源的定义 1.2.1.开源软件优点 1.2.2.经典开源软件案例 1.3.关于开源协议 1.3.1.如何选择开源协议 2.如何查找开源项目 2 ...

  5. Android 模块打包生成aar远程坐标(sdk)

    Android 模块打包生成aar远程坐标 打包成AAR到本地仓库 在模块的gradle文件中加入apply plugin: 'maven'  *repository(url:"file:/ ...

  6. ;~ 小部分AutoHotkey脚本源代码测试模板样板.ahk

    ; ;~ 小部分AutoHotkey脚本源代码测试模板样板.ahk ;~ 请把一行或几行少量代码放到此文件中实际测试一下,;~ 看看测试结果如何,等到能够实现代码功能时再复制到自己的脚本代码文件中;~ ...

  7. SpringCloud升级之路2020.0.x版-6.微服务特性相关的依赖说明

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford spring-cl ...

  8. for,while,until三种循环

    目录 一.echo命令-改变输出字符串或者提取shell变量的值 1.1..格式 2.2.常用参数 二.for循环语句 2.1.for循环结构 示例1 示例2 三.while循环语句结构 示例1 示例 ...

  9. squid异常停止的排查步骤

    今天重启squid的时候发现,squid启动后,status 一会就stop了 whoami@blackman:~/script/AutoProxy-master/main/server$ sudo ...

  10. Java数组02——三种初始化及内存分析

    内存分析 三种初始化 例子  package array; ​ public class ArrayDemon02 {     public static void main(String[] arg ...