【发布时间】: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