我们在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
我们在检查文件时尝试了很多变种:
这些事情似乎都没有任何区别。我们没有对每个选项进行大量测试,但是在文件存在检查通过之前,每个选项似乎都需要花费很长时间。
有一件事做得很好。在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()都是次优解。
答案 0 :(得分:2)
这是发生了什么。 PHP stat缓存依赖于atime属性,该属性可从基础VFS获得。当NFS为VFS供电时,属性会受到缓存以减少服务器往返。遗憾的是,这些可能会导致PHP对状态“撒谎”,因为实际上NFS服务器还没有给出VFS当前信息。
您可以强制立即与noac
挂载选项保持一致。我建议在任何服务器上使用它,绝对,积极地,在最短的时间内需要最新的信息:
使用noac挂载选项实现多个客户端之间的属性缓存一致性。几乎每个文件系统操作都会检查文件属性信息。客户端将此信息缓存一段时间以减少网络和服务器负载。当noac生效时,会禁用客户端的文件属性缓存,因此每个需要检查文件属性的操作都会被强制返回服务器。这允许客户端以非常快的速度查看文件的更改,但代价是许多额外的网络操作。
如果noac
太慢,还有其他挂载选项可能会更好地调整缓存以满足您的需求。请参阅:lookupcache
和actimeo
。例如,减少actimeo
将降低NFS本地缓存信息的时间:默认值为30秒(最小值)到60秒(最大值)。或者,作为另一个例子,lookupcache=positive
将提供关于新文件外观的更快的情报,但即使在取消链接之后也会长期缓存它们。
但是,为什么在没有这些挂载选项时,目录中的ls
“修复”了这个问题?事实证明opendir
和closedir
序列使NFS属性缓存无效,这会强制回调服务器。
因此,在您的情况下,您使用opendir()/closedir()
序列来使缓存无效。我不确定system("ls")
是否可行,因为我相信每个进程都有不同的底层属性缓存视图,但值得一试。