LoginSignup
18
20

More than 5 years have passed since last update.

curl_multi_select() > 0なら無事通信が終わったら報告してくれる・・・とは限らない!

Posted at

curl_multiでHTTP並列リクエストを行うサンプル
に助けられながらcurl_multi_*と楽しくダンスしていたのだが、ちょっとお洒落な事をしようとしたら上手くいかない!

やりたかった事

curl_multi_selectって待ってくれるんでしょ?んじゃループで回さないで、$this->mhにcurl_multiハンドラ持たせておいて、

curl_benriクラス
class curl_benri {
    function startMultiRequest(){
        $this->mh = curl_multi_init();
        curl_multi_add_handle($this->mh, $ch1);
        curl_multi_add_handle($this->mh, $ch2);
        curl_multi_add_handle($this->mh, $ch3);
        curl_multi_add_handle($this->mh, $ch4);
        // アクセスの実行
        do{
            $stat = curl_multi_exec($this->mh, $this->still_running);
        }while($stat === CURLM_CALL_MULTI_PERFORM);
    }

    function getContents(){
        curl_multi_select($this->mh, $timeout);
        do{
            $stat = curl_multi_exec($this->mh, $still_running);
        }while($stat === CURLM_CALL_MULTI_PERFORM);
        $info = curl_multi_info_read($mh, $remains);
        return curl_multi_getcontent($info["handle"]);
    }
}

って用意したら

使うがわ
$curl_benri = new curl_benri();
$curl_benri->startMultiRequest();
while($content = $curl_benri->getContents()){
    // ここで$contentをほにゃらら
}

で楽に処理出来るかと思ってた。
ところがどっこい、これだと$infoに容易くfalseが入って上手く動かない。というかcurl_multi_selectが0.02秒くらいで戻ってくる!なんで!?お前待ってるって言ったよね!!?

何が起こったか

curl_multi_selectは確かに待ってくれる献身的な奴だった。だがしかし、奴が待つのはHTTPのレスポンスではない。あくまでもアクティビティ、イベントなのだ!

CURLOPT_VERBOSEしてみました

> GET /honyarara.html HTTP/1.1
Host: example.com
Accept: */*

$

・・・GETはしてるけどレスポンス待たないで終わった。

そう、つまりcurl_multi_selectはちゃんと待っていた。そして僕らに教えてくれたのだ。
「GET投げ終わったよ!褒めて褒めて!」

どうしたか

curl_benriクラス
class curl_benri {
    // 〜省略〜
    function getContents(){
        do if($this->remains > 0 || curl_multi_select($this->mh, $timeout) != -1){
            do{
                $stat = curl_multi_exec($this->mh, $still_running);
            }while($stat === CURLM_CALL_MULTI_PERFORM);
            $info = curl_multi_info_read($mh, $this->remains);
            if($info === false) continue;
            return curl_multi_getcontent($info["handle"]);
        }while($still_running);
    }
}

selectをループでぶん回した。
selectを抜けてもcurl_multi_info_readがfalseを返す事が多いので(先述のリクエスト完了等)、falseの場合には再度ループを回してselect氏に待ってもらう事にした。
合わせてcurl_multi_info_readの残りのキューがあれば、selectをすっ飛ばしてとりに行くようにもした。これをやらないと、他のところで処理して戻って来た時にselect氏は「2番と3番のデータが来ました!」とまとめて報告してくれる。そうなれば2番だけデータを受け取ってこのループから抜けたが最後、「さっきので報告は終わりです!他ありません!」と言われて3番をとりに行けないからだ。そんな場合でもcurl_multi_info_readは「二件のメッセージをお預かりしています。一件目〜・・・残りのメッセージは一件です。」と留守番電話並みによくできた対応をしてくれるので、この残り件数をメモっておけばselectに聞くまでもなくデータが取得できる。(curl_multi_execが無駄に呼ばれるのはご愛嬌)

これにほぼ一日使ってしまった。俺のような犠牲者がもう出ない事を祈りここに記す。

18
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
20