Yii 2 Frameworkを使用してWebアプリケーションを作成しています。Apache Webサーバー上でPHPを実行しています(サーバーはUbuntuです)。exec()は非同期でなければなりませんが、
アプリの大きな部分は、ユーザーがビデオをアップロードすることです。そのビデオはFFMpegを介して2回、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;
}
シェルコマンドの終わりには、PHPを許可する、コマンドは非同期に実行させるべきでは/ dev/nullにリダイレクトを見ることができますスクリプトを続行し、アップロードしたビデオIDをコントローラに返します。あなたが伝えることができるかもしれませんが、私はffmpegのを呼び出すためにPHP-FFmpegのライブラリ(https://github.com/PHP-FFMpeg/PHP-FFMpeg)を使用しています、と私は
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;
}
:
は、ここでのVideoController法actionProcessのやや短縮バージョンですImagickを実装するためにtpmancのYii2 Imagickライブラリ(https://github.com/tpmanc/yii2-imagick)を使用します。
したがって、exec()コマンドは非同期に実装されるべきですが、そうではありません。ビデオをアップロードすると、ビデオがアップロードされ、ビデオ処理が完了してから最後に「正常にアップロードされました」ページがロードされるまで、さらに5〜10分間待機します。
ここが問題です:それは働いていました。私は開発サイクルの早い段階でそれをテストしました。次に、シェルコマンドの/ dev/nullリダイレクトをコメントアウトして、開発中にデバッグできるようにしました。今度はそれを追加したので、もう動作していないようです。上記のコマンドが非同期に実行されない原因は何ですか?
EDIT:唯一の変更は、コンソールで実行されたPHPスクリプトに加えられたものです。実際のコマンド自体を実行するスクリプト(アップロードフォームモデル)は、スクリプトが動作したときと実行されなかったときの間で変更されませんでした。だから私は紛れているかのような明らかなタイプミスがあるか、コンソールコマンドの何かが/ dev/nullのリダイレクトを無効にして、スクリプトが完了するのを待つようにフォームモデルを強制します。もちろん、私はその数に間違っている可能性があります。
更新:ビデオをアップロードするたびに手動でスクリプトを呼び出すのではなく、これを達成するためにCronを使用して巻き上げました。つまり、実際の質問はまだ解決されていないので、この質問はまだ開いておくべきだと思います。なぜ、上記のexec()が非同期で実行されないのでしょうか?
最終編集:Welp、ここでdownvoteの妖精が来る。閉じた質問を考えてみましょう。
* "コマンドを非同期で実行させるはずの/ dev/nullリダイレクトを見ることができます" *。まあまあ。コマンドの末尾にあるアンパサンドは、バックグラウンドでコマンドを実行するように* shell *に指示します。バックグラウンドタスクを実行する場合は、Celeryのような実際の非同期タスクキューを使用する必要があります。 –