【问题标题】:Testing for disk cache buffers being flushed测试正在刷新的磁盘缓存缓冲区
【发布时间】:2013-11-21 17:13:38
【问题描述】:

我目前有一个视频文件正在通过shell_exec() 调用转换为不同的格式。调用或格式转换没有问题,一切正常;但我的下一步是将该文件推送到 s3 存储桶。

但是,我注意到文件系统缓存不一定会立即刷新我新写入的文件,所以我将一个 0 字节文件推送到 s3,即使我在文件系统上查看它时也是正确的长度。在调用 shell_exec 和 s3-push 之间在我的代码中插入任意 5 秒睡眠解决了这个问题,但感觉非常糟糕,而且我无法知道 5 秒睡眠是否总是足够的,尤其是在使用更大的视频文件,系统正在加载中。

我很确定我不能强制刷新磁盘缓存,除非我执行同步调用(再次通过 shell_exec),但我不想使用这种方法,因为它会影响服务器上的所有文件任何缓冲数据,而不仅仅是我正在操作的单个文件。

所以我编写了这段简单的代码来监控文件大小,直到完成任何磁盘缓存刷新:

$prevSize = -1;
$size = filesize($myFileName);
while ($prevSize < $size) {
    sleep(1);
    clearstatcache(true, $myFileName);
    if ($size > 0)
        $prevSize = $size;
    $size = filesize($myFileName);
}

基本上,只是循环直到至少有一些东西被刷新到文件中,并且文件大小已经保持一致至少一秒钟。

我不知道的是磁盘刷新是否只有在所有文件缓存都成功刷新到磁盘时才会更新大小;或者它是否会一次刷新几个块,我可能会发现自己试图将部分刷新的文件推送到 s3 并最终导致它被损坏。

任何建议将不胜感激。

编辑

现有代码如下所示:

private static function pushToS3($oldFilePath, $s3FileName, $newFilePath) {
    self::testFileFlush($newFilePath);
    file_put_contents(
        $s3FileName,
        file_get_contents($newFilePath)
    );
}

private function processVidoe($oldFilePath, $s3FileName, $newFilePath) {
    // Start Conversion
    $command = "ffmpeg -i \"$oldFilePath\" -y -ar 44100 \"$newFilePath\"";
    $processID = shell_exec("nohup ".$command." >/dev/null & echo $!");

    self::pushToS3($oldFilePath, $s3FileName, $newFilePath);
    unlink($newFilePath);
    unlink($oldFilePath);
}

这是对在单个服务器上运行的旧遗留代码的修改,只是将文件存储在服务器的文件系统中;但是我已经更改了基础架构以在多个 AWS EC2 应用程序服务器上运行以实现弹性,并使用 S3 在 EC2 之间提供文件资源共享。文件由我们的用户上传到应用服务器,然后转换为 flv 并推送到 S3,以便所有 EC2 实例都可以使用它们。

长期解决方案将使用 AWS Elastic Transcoder,那时我可以简单地将原始数据推送到 S3 并向 Elastic Transcoder 提交排队请求,但这还需要一段时间。

【问题讨论】:

  • 暂无评论,我忘了&amp; 是隐含的;。您正在后台进行转换并捕获 PID,为什么不测试该 PID 是否仍在运行而不是与文件大小混在一起呢? if( shell_exec("ps -p $processID &gt;/dev/null; echo $?") === "0" ) { echo "still running"; } 就是说,为什么要让进程后台运行,然后等待它完成?
  • 目前,我需要等待它完成,因为我使用相同的代码块将结果文件推送到 S3 - 从长远来看,我几乎肯定会切换到 Elastic Transcoder 并将其视为排队作业或 gearman 任务(对于其他上传的文件,我与 LibreOffice 有类似的情况)......目前,我需要一个快速的解决方案(不是总是这样吗? )
  • 但循环直到 ffmpeg 进程终止肯定比我不确定等到文件大小达到稳定时更好的解决方案
  • 老实说,我会在我的回答中走这条路线,将第二个脚本分叉到后台来处理带有错误处理和后续 S3 上传的转码。 IMO 它更符合您使用 ETS/Gearman 的最终目标,不会让用户在转码发生时等待,并且可以完全了解 ffmpeg 返回错误/状态信息。

标签: php flush diskcache


【解决方案1】:

除非您正在执行以下操作之一,否则您所描述的行为应该是不可能的:

  1. 将数据写入临时文件,然后将其复制/移动到您尝试上传的位置。
  2. 用两台不同的机器装载同一个分区,一台写入文件,另一台尝试上传文件。
  3. 正在发生某种骇人听闻的软件缓冲。

否则,FS 缓存应该完全对操作系统上运行的任何东西都是透明的,并且任何对尚未写入磁盘的缓存数据的请求都将由操作系统从缓存中提供。 p>

在 #2 的情况下,您应该能够通过将缓存方法更改为直写而不是回写来在某种程度上解决它。您的写入性能下降,但始终会立即写入数据,并且您面临数据丢失的风险要小得多。

编辑

Ffmpeg 可能正在触摸您给它的文件名,使用临时文件[s] 转换视频,然后将完成的文件移动到目标。我假设触发转换的脚本是该过程的背景,否则不会对已完成的文件是否存在产生任何混淆。

我的建议是,不要只将 ffmpeg 分叉到后台进程,然后测试是否存在最终文件,而是在后台调用 ffmpeg 将其分叉到另一个 PHP 脚本中, 然后在完成后触发上传。

例如:

//user-facing.php
<?php
echo "Queueing your file for processing..."
shell_exec("/usr/bin/php /path/to/process.php /path/to/source.mpg /path/to/dest.mpg &")
echo "Done!"

和:

//process.php
<?php
exec(sprintf("/path/to/ffmpeg -options %s %s", $argv[1], $argv[2]), $output, $exit_code);
if($exit_code === 0) {
  upload_to_s3($argv[2]);
} else {
  //notify someone of the error
}

这还可以让您从 ffmpeg 捕获输出和返回代码并对其采取行动,而不是想知道为什么有些视频只是默默地无法转换。

【讨论】:

  • 该文件不是由 PHP 编写的:它是由 ffmpeg 创建的,通过 shell_exec() 调用执行;然后使用 PHP 中的 s3 文件流上下文执行 file_put_contents(s3, file_get_contents(由 ffmpeg 调用生成的文件)) 将其传输到 s3 存储桶.. 它肯定会将 0 字节文件传输到 s3 除非我放代码中的延迟;我假设这是一个文件系统缓存刷新问题,因为我想不出任何其他解释
  • @MarkBaker 根据对您的代码的一些假设,在我的答案中添加了更多内容。如果我的假设是错误的,很高兴看到您的代码。 :我
【解决方案2】:

你在这里看到的不是磁盘缓存的效果。磁盘缓存是透明的——它的行为对用户是不可见的。

您在此处看到的是创建文件但不立即写入文件的应用程序的行为。您将需要找出其他方法来检查它是否已完成。

【讨论】:

  • 如果不是磁盘缓存导致问题,那么我愿意接受建议....但据我所知,对 ffmpeg 的 shell_exec() 调用应该已经完成在 PHP 继续执行推送到 s3 之前
  • 您使用的文件系统或您正在运行的命令有什么奇怪的地方吗?
  • ext4(我相信)没有检查
  • df -T检查并确认,它是ext4
猜你喜欢
  • 2016-12-21
  • 2010-09-15
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 1970-01-01
  • 2011-12-18
  • 2019-10-26
  • 2023-04-01
相关资源
最近更新 更多