PHP结合Inotify实现文件系统实时监控

概述

监控文件系统的变化,不是一个常见的需求,但是随着对PHP使用的深入,不可避免的会碰到这类问题。通常使用文件轮询的通知机制,但是这种机制只适用于经常改变的文件。其他情况下都非常低效,并且有时候会丢失某些类型的变化。

Inotify是什么

Inotify是一种文件变化通知机制,Linux内核从2.6.13开始引入。它可以高效地实时跟踪Linux文件系统的变化。

Inotify-tools 工具包

此工具包是Linux下C语言编写的一个简单的命令行工具包。在CentOS下通过以下命令安装:

yum install inotify-tools

假如我们打算监控/srv/test文件夹上的操作,只需执行:

inotifywait -rme modify,attrib,move,close_write,create,delete,delete_self /srv/test

上述任务运行的同时,我们在另一个shell里依次执行以下操作:创建文件夹,然后在新文件夹下创建文件,接着删除新创建的文件:

% mkdir /srv/test/infoq
% echo TODO > /srv/test/infoq/article.txt
% rm /srv/test/infoq/article.txt

在运行inotifywait的shell中将会打印以下信息:

/srv/test/ CREATE,ISDIR infoq
/srv/test/infoq/ CREATE article.txt
/srv/test/infoq/ MODIFY article.txt
/srv/test/infoq/ CLOSE_WRITE,CLOSE article.txt
/srv/test/infoq/ DELETE article.txt

显而易见,只要有变化我们就会收到相关的通知.其它用法请参考相关文档

在PHP中调用Inotify库

inotify在PHP中使用也很简单,使用inotify_init创建一个句柄,然后通过inotify_add_watch/inotify_rmwatch增加/删除对文件和目录的监听。PHP中提供了inotify扩展,支持了inotify系统调用

安装inotify扩展

pecl install inotify

操作成功后,修改php.ini,加入

extension=inotify.so

查看扩展是否加载成功:

php -m | grep inotify

使用

首先在当前目录创建一个inotify.data文件,示例就用来监听此文件

//创建一个inotify句柄
$fd = inotify_init();
//监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS 
$watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY);  
while (true) {     
    //阻塞地读取数据     
    $events = inotify_read($fd);     
    if ($events) {         
        foreach ($events as $event) {             
            echo "inotify Event :".var_export($event, 1)."\n";         
        }     
    } 
} 
//释放inotify句柄 
inotify_rm_watch($fd, $watch_descriptor); 
fclose($fd); 

修改inotify.data,就可以看到程序输出了信息。

echo "hello world" > inotify.data 

inotify Event :array (   
    'wd' => 1,   
    'mask' => 2,   
    'cookie' => 0,   
    'name' => '', 
)

使用swoole实现异步非阻塞

//创建一个inotify句柄 
$fd = inotify_init(); 
//监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS 
$watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY); 

//加入到swoole的事件循环中 
swoole_event_add($fd, function ($fd) {     
    $events = inotify_read($fd);     
    if ($events) {         
        foreach ($events as $event) {             
            echo "inotify Event :" . var_export($event, 1) . "\n";         
        }     
    } 
});

这里使用了swoole扩展提供swoole event add函数,将inotify句柄设置为非阻塞,并加入到epoll事件循环中。程序变成异步非阻塞模式。当有事件发生时才会执行inotify_ read获取事件。没有事件发生时,程序可以执行其他的逻辑。

总结

Inotify为Linux提供了一套高效监控和跟踪文件变化的机制,它可以实时地处理、调试以及监控文件变化,而轮询是一种延迟机制。PHP通过扩展的方式对Inotify提供了支持,可以方便使用。