web-dev-qa-db-ja.com

PHPの非同期関数呼び出し

私はPHP Webアプリケーションで作業していますが、ユーザーのリクエストに基づいてリモートサーバーから誰かを取得するなど、リクエストでネットワーク操作を実行する必要があります。

関数にデータを渡す必要があり、そこからの出力も必要な場合、PHPの非同期動作をシミュレートすることは可能ですか?.

私のコードは次のようなものです:

<?php

     $data1 = processGETandPOST();
     $data2 = processGETandPOST();
     $data3 = processGETandPOST();

     $response1 = makeNetworkCall($data1);
     $response2 = makeNetworkCall($data2);
     $response3 = makeNetworkCall($data3);

     processNetworkResponse($response1);
     processNetworkResponse($response2);
     processNetworkResponse($response3);

     /*HTML and OTHER UI STUFF HERE*/

     exit;
?>

各ネットワーク操作が完了するまでに約5秒かかり、3つの要求を行うと、アプリケーションの応答時間に合計15秒が追加されます。

MakeNetworkCall()関数は、HTTP POSTリクエストを行うだけです。

リモートサーバーはサードパーティのAPIであるため、そこを制御することはできません。

PS:AJAXまたはその他のことについての提案を答えてはいけません。私は現在、PHPを介してこれを実行できるかどうかを探しています。C++拡張機能などを使用できます。

72
Hardeep Singh

最近では、スレッドよりも queues を使用する方が良いです(Laravelを使用しない人には、他にもたくさんの実装があります このように )。

基本的な考え方は、元のPHPスクリプトがタスクまたはジョブをキューに入れることです。次に、キュージョブワーカーを別の場所で実行し、キューからジョブを取り出して、元のPHPから独立して処理を開始します。

利点は次のとおりです。

  1. スケーラビリティ-ワーカーノードを追加するだけで、需要に対応できます。このようにして、タスクは並行して実行されます。
  2. 信頼性-RabbitMQ、ZeroMQ、Redisなどの最新のキューマネージャーは、非常に信頼性の高いものになっています。
11
aljo f

直接的な答えはありませんが、次のことを調べてください。

6

ここでは、cURLが唯一の本当の選択肢になります(それ、または非ブロッキングソケットといくつかのカスタムロジックを使用します)。

このリンク は正しい方向にあなたを送ります。 PHPには非同期処理はありませんが、同時に複数のWeb要求を作成しようとしている場合、cURL multiがそれを処理します。

3
Colin M

HTMLやその他のUIでデータを返す必要がある場合、それを非同期化する方法はないでしょう。

PHPでこれを行う唯一の方法は、リクエストをデータベースに記録し、1分ごとにcronをチェックするか、Gearmanキュー処理のようなものを使用するか、コマンドラインプロセスをexec()することだと思います

それまでの間、phpページは、数秒ごとにリロードして進行状況を確認するためのHTMLまたはjsを生成する必要がありますが、これは理想的ではありません。

問題を回避するために、いくつの異なるリクエストを期待していますか?それらをすべて1時間ごとに自動的にダウンロードして、データベースに保存できますか?

1
CodeMonkey

Curlのラッパーであるhttp v2もあります。 peclを介してインストールできます。

http://devel-m6w6.rhcloud.com/mdref/http/

0
Erik van Velzen

ここでcURLソリューションに関するコードが必要だと思うので、私は共有します(複数のソースをPHPマニュアルとコメントとして混合して書かれています)。

いくつかの並列HTTP要求($aURLsのドメイン)を実行し、各要求が完了すると応答を出力します(他の可能な用途のために$doneに格納します)。

リアルタイムの印刷部分と過剰なコメントのため、コードは必要以上に長くなっていますが、回答を編集して改善してください。

<?php
/* Strategies to avoid output buffering, ignore the block if you don't want to print the responses before every cURL is completed */
ini_set('output_buffering', 'off'); // Turn off output buffering
ini_set('zlib.output_compression', false); // Turn off PHP output compression       
//Flush (send) the output buffer and turn off output buffering
ob_end_flush(); while (@ob_end_flush());        
Apache_setenv('no-gzip', true); //prevent Apache from buffering it for deflate/gzip
ini_set('zlib.output_compression', false);
header("Content-type: text/plain"); //Remove to use HTML
ini_set('implicit_flush', true); // Implicitly flush the buffer(s)
ob_implicit_flush(true);
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
$string=''; for($i=0;$i<1000;++$i){$string.=' ';} output($string); //Safari and Internet Explorer have an internal 1K buffer.
//Here starts the program output

function output($string){
    ob_start();
    echo $string;
    if(ob_get_level()>0) ob_flush();
    ob_end_clean();  // clears buffer and closes buffering
    flush();
}

function multiprint($aCurlHandles,$print=true){
    global $done;
    // iterate through the handles and get your content
    foreach($aCurlHandles as $url=>$ch){
        if(!isset($done[$url])){ //only check for unready responses
            $html = curl_multi_getcontent($ch); //get the content           
            if($html){
                $done[$url]=$html;
                if($print) output("$html".PHP_EOL);
            }           
        }
    }
};

function full_curl_multi_exec($mh, &$still_running) {
    do {
      $rv = curl_multi_exec($mh, $still_running); //execute the handles 
    } while ($rv == CURLM_CALL_MULTI_PERFORM); //CURLM_CALL_MULTI_PERFORM means you should call curl_multi_exec() again because there is still data available for processing
    return $rv;
} 

set_time_limit(60); //Max execution time 1 minute

$aURLs = array("http://domain/script1.php","http://domain/script2.php");  // array of URLs

$done=array();  //Responses of each URL

    //Initialization
    $aCurlHandles = array(); // create an array for the individual curl handles
    $mh = curl_multi_init(); // init the curl Multi and returns a new cURL multi handle
    foreach ($aURLs as $id=>$url) { //add the handles for each url        
        $ch = curl_init(); // init curl, and then setup your options
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // returns the result - very important
        curl_setopt($ch, CURLOPT_HEADER, 0); // no headers in the output
        $aCurlHandles[$url] = $ch;
        curl_multi_add_handle($mh,$ch);
    }

    //Process
    $active = null; //the number of individual handles it is currently working with
    $mrc=full_curl_multi_exec($mh, $active); 
    //As long as there are active connections and everything looks OK…
    while($active && $mrc == CURLM_OK) { //CURLM_OK means is that there is more data available, but it hasn't arrived yet.  
        // Wait for activity on any curl-connection and if the network socket has some data…
        if($descriptions=curl_multi_select($mh,1) != -1) {//If waiting for activity on any curl_multi connection has no failures (1 second timeout)     
            usleep(500); //Adjust this wait to your needs               
            //Process the data for as long as the system tells us to keep getting it
            $mrc=full_curl_multi_exec($mh, $active);        
            //output("Still active processes: $active".PHP_EOL);        
            //Printing each response once it is ready
            multiprint($aCurlHandles);  
        }
    }

    //Printing all the responses at the end
    //multiprint($aCurlHandles,false);      

    //Finalize
    foreach ($aCurlHandles as $url=>$ch) {
        curl_multi_remove_handle($mh, $ch); // remove the handle (assuming  you are done with it);
    }
    curl_multi_close($mh); // close the curl multi handler
?>
0