web-dev-qa-db-ja.com

PHP HTTP応答送信後の実行を続行

どうすればPHP 5.2(Apache mod_phpとして実行)がクライアントに完全なHTTP応答を送信し、さらに1分間操作を実行し続けることができますか?

長い話:

PHPいくつかの長いデータベースリクエストを実行し、実行に45〜60秒かかる電子メールを送信する必要があるスクリプト。このスクリプトは、制御できないアプリケーションによって呼び出されます。 PHPスクリプト(主に無効なパラメーターエラー)から受信したエラーメッセージを報告するアプリケーションが必要です。

アプリケーションのタイムアウト遅延は45秒より短く(正確な値はわかりません)、したがって、PHPスクリプトのすべての実行をエラーとして登録します。したがって、PHPを使用して、完全なHTTP応答を可能な限り高速にクライアントに送信し(理想的には、入力パラメーターが検証されるとすぐに)、データベースおよび電子メール処理を実行します。

Mod_phpを実行しているので、pcntl_fork 利用できません。処理するデータをデータベースに保存し、cronから実際のプロセスを実行することでこれを回避できますが、より短い解決策を探しています。

60
Victor Nicollet

最初の要求を処理するスクリプトに処理キューにエントリを作成させ、すぐに戻ります。次に、キューで保留中のジョブを定期的に実行する別のプロセスを(cronを介して)作成します。

25
Alex Howansky

「特別なスクリプト」ツールボックスにこのスニペットがありましたが、失われました(当時はクラウドは一般的ではありませんでした)。ここに戻って投稿してください:

<?php
 ob_end_clean();
 header("Connection: close");
 ignore_user_abort(); // optional
 ob_start();
 echo ('Text the user will see');
 $size = ob_get_length();
 header("Content-Length: $size");
 ob_end_flush(); // Strange behaviour, will not work
 flush();            // Unless both are called !
 session_write_close(); // Added a line suggested in the comment
 // Do processing here 
 sleep(30);
 echo('Text user will never see');
?>

実際にいくつかの場所で使用しています。そして、それは完全に理にかなっています:銀行リンクは成功した支払いのリクエストを返しています、そして、私はそれが起こるとき多くのサービスを呼び出して、多くのデータを処理しなければなりません。これには10秒以上かかることがありますが、バンクリンクのタイムアウト期間は固定されています。だから、私は銀行リンクを認め、彼に道を示し、彼がすでにいなくなったときに私の仕事をします。

46
povilasp

必要なのはこの種のセットアップです

alt text

7
Yanick Rochon

自分または他のスクリプトに「httpフォーク」を使用できます。私はこのようなことを意味します:

// parent sript, called by user request from browser

// create socket for calling child script
$socketToChild = fsockopen("localhost", 80);

// HTTP-packet building; header first
$msgToChild = "POST /sript.php?&param=value&<more params> HTTP/1.0\n";
$msgToChild .= "Host: localhost\n";
$postData = "Any data for child as POST-query";
$msgToChild .= "Content-Length: ".strlen($postData)."\n\n";

// header done, glue with data
$msgToChild .= $postData;

// send packet no oneself www-server - new process will be created to handle our query
fwrite($socketToChild, $msgToChild);

// wait and read answer from child
$data = fread($socketToChild, $dataSize);

// close connection to child
fclose($socketToChild);
...

子スクリプト:

// parse HTTP-query somewhere and somehow before this point

// "disable partial output" or 
// "enable buffering" to give out all at once later
ob_start();

// "say hello" to client (parent script in this case) disconnection
// before child ends - we need not care about it
ignore_user_abort(1);

// we will work forever
set_time_limit(0);

// we need to say something to parent to stop its waiting
// it could be something useful like client ID or just "OK"
...
echo $reply;

// Push buffer to parent
ob_flush();

// parent gets our answer and disconnects
// but we can work "in background" :)
...

主なアイデアは次のとおりです。

  • ユーザー要求によって呼び出される親スクリプト。
  • parentは同じサーバー(または他のサーバー)で子スクリプト(親または別のサーバーと同じ)を呼び出し、要求データをそれらに渡します。
  • 親はユーザーに「OK」と言って終了します。
  • 子供が働きます。

子とやり取りする必要がある場合-DBを「通信媒体」として使用できます。親は子ステータスを読み取り、コマンドを書き込むことができ、子はコマンドを読み取り、ステータスを書き込むことができます。複数の子スクリプトで必要な場合-ユーザー側で子IDを保持してそれらを識別し、各子のステータスを確認するたびにそのIDを親に送信する必要があります。

私はここでそれを見つけました- http://linuxportal.ru/forums/index.php/t/22951/

6
SomeGuy

ファイルサーバーでスクリプトを呼び出して、コマンドラインでトリガーされたかのように実行するのはどうでしょうか。これを行うには、PHPの exec を使用します。

5
SorcyCat

何かを実行するPHP function register-shutdown-function を使用できますafterスクリプトはブラウザとのダイアログを完了しました。

ignore_user_abort も参照してください。ただし、register_shutdown_functionを使用する場合、この関数は必要ありません。同じページで、set_time_limit(0)はスクリプトのタイムアウトを防ぎます。

4
Ring Ø

キュー、exec、またはcronを使用することは、この単純なタスクに対するやり過ぎです。同じスクリプト内に留まらない理由はありません。この組み合わせは、私にとってはうまくいきました。

        ignore_user_abort(true);
        $response = "some response"; 
        header("Connection: close");
        header("Content-Length: " . mb_strlen($response));
        echo $response;
        flush(); // releasing the browser from waiting
        // continue the script with the slow processing here...

続きを読む: PHPでajaxリクエストに応答した後にプロセスを続行する方法?

3
Nir O.

サーバーとサーバー間でhttp要求を作成できます。 (ブラウザは必要ありません)。バックグラウンドHTTP要求を作成する秘密は、非常に小さなタイムアウトを設定することであるため、応答は無視されます。

これは私がその目的のために使用した作業関数です:

5月31日PHP=非同期バックグラウンド要求PHP(バックグラウンドモードのシミュレーション)で非同期要求を作成する別の方法。

 /**
  * Another way to make asyncronous (o como se escriba asincrono!) request with php
  * Con esto se puede simpular un fork en PHP.. nada que envidarle a javita ni C++
  * Esta vez usando fsockopen
  * @author PHPepe
  * @param  unknown_type $url
  * @param  unknown_type $params
  */
 function phpepe_async($url, $params = array()) {
  $post_params = array(); 
     foreach ($params as $key => &$val) {
       if (is_array($val)) $val = implode(',', $val);
         $post_params[] = $key.'='.urlencode($val);
     }
     $post_string = implode('&', $post_params);

     $parts=parse_url($url);

     $fp = fsockopen($parts['Host'],
         isset($parts['port'])?$parts['port']:80,
         $errno, $errstr, 30);

     $out = "POST ".$parts['path']." HTTP/1.1\r\n";
     $out.= "Host: ".$parts['Host']."\r\n";
     $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
     $out.= "Content-Length: ".strlen($post_string)."\r\n";
     $out.= "Connection: Close\r\n\r\n";
     if (isset($post_string)) $out.= $post_string;

     fwrite($fp, $out);
     fclose($fp);
 }

 // Usage:
 phpepe_async("http://192.168.1.110/pepe/feng_scripts/phprequest/fork2.php");

詳細については、 http://www.phpepe.com/2011/05/php-asynchronous-background-request.html をご覧ください。

2
Ignacio Vazquez

そのために、非常に短いタイムアウトでcURLを使用することができます。これがメインファイルになります。

_<?php>
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://example.com/processor.php");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10);      //just some very short timeout
    curl_exec($ch);
    curl_close($ch);
?>
_

そして、これはあなたのプロセッサフ​​ァイル:

_<?php
    ignore_user_abort(true);                       //very important!
    for($x = 0; $x < 10; $x++)                     //do some very time-consuming task
        sleep(10);
?>
_

ご覧のように、上のスクリプトは短時間(この場合は10ミリ秒)後にタイムアウトします。 _CURLOPT_TIMEOUT_MS_がこのように機能しない可能性があります。この場合、curl_setopt($ch, CURLOPT_TIMEOUT, 1)と同等です。

そのため、プロセッサフ​​ァイルにアクセスすると、ユーザー(つまり、呼び出し元のファイル)が接続を中止したかどうかに関係なく、プロセッサフ​​ァイルはタスクを実行します。

もちろん、ページ間でGETまたはPOSTパラメーターを渡すこともできます。

1
sigalor

ああ、あなたの要求を誤解しました。彼らは実際にいるように見えます:

  • スクリプトは、制御していない外部ソースから入力を受け取ります
  • スクリプトは入力を処理および検証し、外部アプリにそれらが良いかどうかを知らせ、セッションを終了します。
  • スクリプトは、長時間実行されるプロセスを開始します。

この場合、はい、外部ジョブキューやcronを使用すると機能します。入力が検証されたら、ジョブの詳細をキューに挿入して終了します。その後、別のスクリプトを実行して、キューからジョブの詳細を取得し、より長いプロセスを開始できます。 Alex Howanskyは正しい考えを持っています。

申し訳ありませんが、私は最初に少しスキミングしました。

0
Ryan Chouinard

これらの機能を3つのスクリプトに分割できます。 1.プロセスを開始し、execまたはcommand経由で2番目のプロセスを呼び出します。これは、http呼び出し経由で実行することもできます。 2. 2番目はデータベース処理を実行し、最後に最後の1つを開始します3.最後の1つは電子メールを送ります

0
Aram

Apacheでphp.ini構成ファイル、出力バッファリングが無効になっていることを確認します。

output_buffering = off
0
Luan Costa

ユーザーとプロセスを続行するのではなく、最後に新しい非同期リクエストを生成することをお勧めします。

次の回答を使用して、他のリクエストを生成できます。 Asynchronous PHP calls?

0
Brent