exec()应该是异步的,但它不是

时间:2016-12-11 02:56:56

标签: php bash server yii2 imagick

我正在使用Yii 2 Framework创建一个Web应用程序,在带有PHP的Apache Web服务器上运行(服务器是Ubuntu)。

应用程序的很大一部分涉及用户上传视频,并且该视频通过FFMpeg运行以重新保存两次,一次作为MP4再次作为WEBM。 FFMpeg还提取一个帧,然后通过Imagick运行以正确调整大小。

所有这些都需要花费大量时间,因此我没有向用户提供可能需要5-10分钟的加载屏幕,而是选择将所有处理放在控制台命令中,然后以异步方式运行在后台,并在视频处理完毕后通过电子邮件发送给他们。

这是上传表单模型的相关部分:

// if the new database entry successfully saved
if($video->save()){

    // define the target filename and full target filepath
    $target_name = uniqid();
    $target_path = Yii::getAlias('@webroot'). '/videos/' . $target_name;

    // get the current working directory (should be /models)
    $cwd = getcwd();

    // move up one directory to the app base 
    chdir('../');

    // prepare the shell command to process the video
    $command = escapeshellcmd("php yii video/process " . $fileFullPath . " " . $target_name . " " . $target_path . " " . $video->id . " " . $video->name . " " . Yii::$app->language . " " . Yii::$app->homeUrl . " " . $this->email . " >/dev/null 2>&1 &");

    // execute the shell command
    exec($command);

    // change the working directory back to the original
    chdir($cwd);

    // return the ID of the uploaded video
    return $video->id;
}

在shell命令结束时,您可以看到应该导致命令异步执行的/ dev / null重定向,允许PHP脚本继续并将上传的视频ID返回给Controller。

这是VideoController方法actionProcess的一个稍微缩短的版本:

public function actionProcess($source_path, $target_name, $target_path, $id, $first_name, $language, $homeUrl, $email)
{
    $ffmpeg = FFMpeg\FFMpeg::create(['timeout' => 7200, 'ffmpeg.threads' => 4]);
    $video = $ffmpeg->open($source_path);
    $dimension = new FFMpeg\Coordinate\Dimension(1280, 1280);

    $video
        ->filters()
        ->resize($dimension, 'inset')
        ->synchronize();

    $video
        ->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(2))
        ->save($target_path . '.png');

    $video
        ->save(new FFmpeg\Format\Video\X264(), $target_path . '.mp4')
        ->save(new FFMpeg\Format\Video\WebM(), $target_path . '.webm');

    @unlink($source_path);

    $image = Imagick::open($target_path . '.png');

    //////////
    // There's a big if statement here controlling whether to crop the image vertically or horizontally to get the desired size.
    // Didn't seem necessary to include.
    //////////

    $image->saveTo($target_path . '.png');

    $video = Video::find()->where(['id' => $id])->one();
    $video->path = $target_name;
    $video->published = Video::IS_PUBLISHED;
    $video->save();

    //////////
    // There's another large code block here to send an email to the user.
    // Also didn't seem necessary to include.
    //////////

    return 0;
}

正如您可能能够说的那样,我使用PHP-FFmpeg库(https://github.com/PHP-FFMpeg/PHP-FFMpeg)来调用FFMpeg,并且我正在使用tpmanc的Yii2 Imagick库({ {3}})实施Imagick。

所以,所有这些都说:exec()命令应该异步实现,但它不是。上传视频会导致视频上传,然后等待5到10分钟,同时视频处理工作并完成,最后加载成功上传的视频'页。

事情就是这样:它正在发挥作用。我在开发周期的早期测试过它很好。然后我在shell命令上注释掉了/ dev / null重定向,这样我就可以在开发时进行调试,现在我已经将它添加回来,它似乎不再起作用了。什么可能导致上述命令不能异步执行?

编辑:我还应该补充说,所做的唯一更改是对控制台执行的PHP脚本进行的。执行实际命令本身的脚本(上载表单模型)在它工作之间以及它没有执行时没有进行任何更改。所以,要么是我错过了明显的拼写错误,或者控制台命令中的某些内容覆盖/ dev / null重定向并强制表单模型等待脚本完成,这不是看起来它实际上应该是可能的,虽然我当然可能错了。

更新:我最后使用Cron完成此操作,而不是每次上传视频时手动调用脚本。也就是说,我认为这个问题应该保持开放,因为实际问题尚未解决:为什么上面的exec()不能异步执行?

最终编辑:韦尔普,这里是downvote仙女。考虑结束这个问题。

1 个答案:

答案 0 :(得分:1)

几年前我和视频有同样的问题。 PHP不是异步的。我建议你使用React PHP(http://reactphp.org/)或者创建JobQueue和/或Cron任务列表来处理它。您可以创建视频并上传,向用户显示视频,显示消息,“渲染”并在未向他创建缩略图时为视频创建示例缩略图。

我使用Yii创建了这个示例到JobQueue,但之后搜索得更好。 https://github.com/yiisoft/yii2-queue