curl_multiでHTTP並列リクエストを行うサンプル
に助けられながらcurl_multi_*と楽しくダンスしていたのだが、ちょっとお洒落な事をしようとしたら上手くいかない!
##やりたかった事
curl_multi_selectって待ってくれるんでしょ?んじゃループで回さないで、$this->mhにcurl_multiハンドラ持たせておいて、
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投げ終わったよ!褒めて褒めて!」
##どうしたか
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が無駄に呼ばれるのはご愛嬌)
これにほぼ一日使ってしまった。俺のような犠牲者がもう出ない事を祈りここに記す。