PHP file_exists 或 is_file 在 NFS 文件 (EC2) 上未正确回答 10-20 秒

PHP file_exists or is_file does not answer correctly for 10-20s on NFS files (EC2)

我们在 EC2 上有一个 nginx/php-fpm 设置,它将文件块接收到 NFS 安装的 "chunk" 文件夹(特别是 SoftNAS),该文件夹在多个应用程序服务器之间共享。我们遇到一个问题,应用程序在将完成的文件上传到 S3 之前检查文件是否存在,但即使文件存在,文件检查也失败。

该应用程序在 is_file() 或 file_exists() 之前有一个 clearstatcache()(我们都试过了)但是该文件对应用程序不可见10-20s.

这是该测试的一些 运行 的输出:

 app1 write timestamp 1484702190.5575
 app2 read timestamp  1484702216.0643
 25.5068 seconds

 app1 write timestamp 1484702229.0130
 app2 read timestamp  1484702246.0652
 17.0522 seconds

 app1 write timestamp 1484702265.6277
 app2 read timestamp  1484702276.0646
 10.4369 seconds

 app1 write timestamp 1484702286.0136
 app2 read timestamp  1484702306.0645
 20.0509 seconds

 app1 write timestamp 1484702314.4844
 app2 read timestamp  1484702336.0648
 21.5804 seconds

 app1 write timestamp 1484702344.3694
 app2 read timestamp  1484702366.0644
 21.6950 seconds

 app1 write timestamp 1484702374.0460
 app2 read timestamp  1484702396.0645
 22.0185 seconds

 app1 write timestamp 1484702404.0346
 app2 read timestamp  1484702426.0647
 22.0301 seconds

 app1 write timestamp 1484702434.2560
 app2 read timestamp  1484702456.1092
 21.8532 seconds

 app1 write timestamp 1484702466.0083
 app2 read timestamp  1484702486.1085
 20.1002 seconds

 app1 write timestamp 1484702496.5466
 app2 read timestamp  1484702516.1088
 19.5622 seconds

 app1 write timestamp 1484702525.2703
 app2 read timestamp  1484702546.1089
 20.8386 seconds

 app1 write timestamp 1484702558.3312
 app2 read timestamp  1484702576.1092
 17.7780 seconds

我们尝试了多种检查文件的方法:

None 这些东西似乎有什么不同。我们没有 运行 对每个选项进行广泛的测试,但在文件存在检查通过之前,每个选项似乎都花费了很长时间。

有一件事确实奏效了。 运行 shell 中 app2 上的 "ls" 循环,app2 脚本可立即读取该文件。

 app1 write timestamp 1484703581.3749
 app2 read timestamp  1484703581.3841
 0.0092 seconds

 app1 write timestamp 1484703638.81 00
 app2 read timestamp  1484703638.8139
 0.0039 seconds

 app1 write timestamp 1484703680.8548
 app2 read timestamp  1484703680.8576
 0.0028 seconds

因此,shell 中的某些内容正在正确清除 NFS 缓存,但 PHP 中的清除缓存命令似乎没有任何区别。

(编辑)有问题的代码:

public static function get($filepath) {

clearstatcache(TRUE, $filepath);

if (file_exists($filepath)) {
    $instance = new static::$_class;
    $instance->init($filepath);
    return $instance;
} else {

    // Sometimes a new file is not found with the first is_file() attempt.
    // Clear the stat cache and try to find the file again.

    clearstatcache(TRUE, $filepath);

    if (file_exists($filepath)) {
        $instance = new static::$_class;
        $instance->init($filepath);
        return $instance;
    }
}

Log::error("AJRFSFILE " . $_SERVER['PATH_INFO'] . " "  . $_SERVER['HTTP_DEVICE'] . " " . $filepath . " " . json_encode(stat($filepath)));

return false;
}

(Edit2) 原来 运行ning exec() 在代码中使用 "ls" 成功清除了系统级发生的任何文件级缓存,但出于显而易见的原因,一个 exec( ) 每次我们做 file_exists 都是一个次优的解决方案。

这是正在发生的事情。 PHP stat 缓存依赖于 atime 属性,该属性可从底层 VFS 获得。当 NFS 为 VFS 提供动力时,属性会受到缓存以减少服务器 round-trips。不幸的是,这些可能导致 PHP 到 "lie" 状态,因为实际上 NFS 服务器没有提供 VFS 当前信息。

您可以使用 noac 安装选项强制立即保持一致。我建议在您绝对肯定地需要在尽可能短的时间内获得最新信息的任何服务器上使用它:

Use the noac mount option to achieve attribute cache coherence among multiple clients. Almost every file system operation checks file attribute information. The client keeps this information cached for a period of time to reduce network and server load. When noac is in effect, a client’s file attribute cache is disabled, so each operation that needs to check a file’s attributes is forced to go back to the server. This permits a client to see changes to a file very quickly, at the cost of many extra network operations.

如果 noac 太慢,还有其他装载选项可能会根据您的需要更好地调整缓存。参见:lookupcacheactimeo。例如,减少 actimeo 将减少 NFS 本地缓存信息的时间:默认值为 30 秒(最小值)到 60 秒(最大值)。或者,作为另一个示例,lookupcache=positive 将提供有关新文件外观的更快智能,但即使在取消链接后也会长期缓存它们的存在。

但是,为什么在没有这些安装选项的情况下,目录 "fix" 中的 ls 会出现问题?事实证明 opendirclosedir 序列使 NFS 属性缓存无效,这会强制调用回服务器。

因此在您的情况下,您使用 opendir()/closedir() 序列使缓存无效。我不确定 system("ls") 是否有效,因为我相信每个进程对底层属性缓存都有不同的看法,但值得一试。