【问题标题】:PHP file_exists or is_file does not answer correctly for 10-20s on NFS files (EC2)PHP file_exists 或 is_file 在 NFS 文件 (EC2) 上的 10-20 秒内无法正确回答
【发布时间】:2017-06-03 01:40:44
【问题描述】:

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

应用在 is_file() 或 file_exists() 之前有一个 clearstatcache()(我们都尝试过),但该文件在 10-20 秒内对应用不可见。

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

 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

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

  • 在检查文件是否存在时使用 is_file 和 file_exists 函数 存在于 app2 上。
  • 使用 clearstatcache 函数的所有变体 在检查文件是否存在于 app2 之前。
  • 在 app1 上写入文件之前先触摸该文件。
  • 在检查 app2 上是否存在文件之前先触摸该文件。
  • 使用不同的方法从 app1 和 显式关闭写入流并释放对 文件。
  • 读取循环之间的不同延迟(例如无延迟或 最多延迟 1 秒)。
  • 在从 app1 写入目录后,使用 exec 对目录进行“ls”。
  • 在每个文件存在之前使用 exec 对目录“ls”检查 app2。

这些事情似乎都没有任何区别。我们没有对每个选项进行广泛的测试,但每个选项似乎都需要很长时间才能通过文件存在检查。

有一件事确实奏效了。在 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) 结果是,在代码中运行带有“ls”的 exec() 成功清除了系统级别发生的任何文件级缓存,但由于显而易见的原因,每次执行 file_exists 时的 exec() 都是次优解。

【问题讨论】:

  • clearstatcache()只清除当前运行的php脚本进程的interl缓存。所以file_exists('file.a');unlink('file.a');file_exists('file.a'); 这里两个 file_exists 都是真的。正确的是 file_exists('file.a');unlink('file.a');clearstatcache();file_exists('file.a'); 现在 seconde file_exists 现在给出 false。也许您对阻止某些操作的文件指针(来自任何地方)有问题。
  • 首先,显示一些代码:没有代码上下文的调试输出是没有用的。其次,您是说is_file(等人)调用blocks 10s-20s?第三,您在“修复”这个问题的 shell 中到底在做什么?
  • 不,is_file() 在 10-20 秒内报告错误。当文件写入 app1 时,“ls”命令在 app2 服务器上的 shell 中运行。运行 "ls" 会使 app2 is_file() 立即返回 true,而不是在 10-20 秒之后。
  • 在没有任何参数的情况下试一试clearstatcache();,也许在下次检查之前做一个microtime(100);
  • 我们的原始代码在没有参数的情况下运行它,结果相同。

标签: php linux nginx amazon-ec2 nfs


【解决方案1】:

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

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

使用 noac 挂载选项来实现多个客户端之间的属性缓存一致性。几乎每个文件系统操作都会检查文件属性信息。客户端将此信息缓存一段时间以减少网络和服务器负载。当 noac 生效时,客户端的文件属性缓存被禁用,因此需要检查文件属性的每个操作都被强制返回到服务器。这允许客户端非常快速地查看对文件的更改,但代价是许多额外的网络操作。

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

但是,当没有这些挂载选项时,为什么目录中的ls 会“修复”问题?原来opendirclosedir 序列使NFS 属性缓存无效,这会强制调用回服务器。

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

【讨论】:

  • 我可以将 noac 与 noatime 和 noadirtime 结合使用,还是会使它们失效?
  • @JeremyWilson 我相信noacnoatimenodiratime 的超集。就个人而言,我只会设置noac
  • 使用 opendir() 解决了 PHP 级别的问题,所以我不需要调整 NFS 参数。谢谢!
  • @JeremyWilson 是的,在大多数情况下最简单的方法。最终可能不是最有效的,但它可以完成工作。
  • 非常感谢 opendir()/closedir() 技巧为我节省了几个小时。如果可以的话,我会投票给你的答案 100 倍。
猜你喜欢
  • 2010-10-22
  • 2013-05-19
  • 1970-01-01
  • 1970-01-01
  • 2016-01-13
  • 2011-06-05
  • 2011-05-05
  • 2012-05-17
  • 1970-01-01
相关资源
最近更新 更多