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

时间:2017-01-18 15:39:16

标签: php linux nginx amazon-ec2 nfs

我们在EC2上有一个nginx / php-fpm设置,它接收文件块到NFS安装的“chunk”文件夹(特别是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中的clear cache命令似乎没有任何区别。

(编辑)有问题的代码:

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()都是次优解。

1 个答案:

答案 0 :(得分:2)

这是发生了什么。 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")是否可行,因为我相信每个进程都有不同的底层属性缓存视图,但值得一试。