curl_multiでHTTP並列リクエストを行うサンプル - Qiita [キータ]で@mpywさんに質問された件…の答えにはなってないんですが、CURLOPT_FILEでレスポンスを丸投げするイメージを書いてみました。
あんまり自信ないです。もうちょっといい書き方がありそうな気がする。
サーバー側
ストリーミングAPI的な感じのサンプル。node.jsで書いてみましたが、クライアントから切断されたらどうするとか書いてないし、ゴミのようなプログラムですね。。(石投げないで><)
node stream.js
と実行しておいて、 curl -v http://localhost:1337/
とかやると1秒ごとにじわじわとレスポンスが出力されていく様子がわかります。
var http = require('http');
http.createServer(function(req, res){
var i = 0;
var id = 100 * Math.random() | 0;
res.writeHead(200, {'Content-Type': 'text/plain'});
setInterval(function(){
var result = id + ':' + ++i;
console.log(result);
res.write(result + '\n');
}, 1000);
}).listen(1337, 'localhost');
console.log('Server running at http://localhost:1337/');
クライアント側
上のAPIっぽいものは無限に出力するので、全部返ってくるまで待っているといつまでも処理ができません。
そこでレスポンスとファイルを直結して直接流し込むような感じにしてみます。
CURLOPT_FILEでfopen()したファイルを直接指定しているのがポイントです。
ここでは通常のfile:を使いましたが、PHPにはストリームという仕組みがあり、ファイルと同じような感じで他の場所に書き込んだり、ストリームラッパーを自作して直接イベント駆動なプログラムが書けたりします。
<?php
const TIMEOUT = 10;
$mh = curl_multi_init();
$ch1 = curl_init('http://localhost:1337/');
$fh1 = fopen('result1.txt', 'w');
curl_setopt($ch1, CURLOPT_FILE, $fh1);
curl_multi_add_handle($mh, $ch1);
$ch2 = curl_init('http://localhost:1337/');
$fh2 = fopen('result2.txt', 'w');
curl_setopt($ch2, CURLOPT_FILE, $fh2);
curl_multi_add_handle($mh, $ch2);
//curl_multi start
do $stat = curl_multi_exec($mh, $running);
while ($stat === CURLM_CALL_MULTI_PERFORM);
if (!$running || $stat !== CURLM_OK) {
throw new RuntimeException("$running $stat");
}
//curl_multi wait
do switch (curl_multi_select($mh, TIMEOUT)) {
case -1: // selectに失敗するケースがあるらしい https://bugs.php.net/bug.php?id=61141
usleep(10);
do $stat = curl_multi_exec($mh, $running);
while ($stat === CURLM_CALL_MULTI_PERFORM);
continue 2;
case 0: //timeout
continue 2;
default:
//何か変化があった
do $stat = curl_multi_exec($mh, $running);
while ($stat === CURLM_CALL_MULTI_PERFORM);
//強制書き込み
fflush($fh1);
fflush($fh2);
} while ($running);
サーバーが起動している状態で、このクライアント側のプログラムを動かすと、result1.txtとresult2.txtに1秒おきにレスポンスが追記されていきます。tail -f result1.txt
とかで様子が見れるはず!
TODO
- ストリーム処理の実装方法
- fflushで強制書き込みするのがダサいし、2つとも書き込みしているのが無駄。本当にイベントが起きたストリームだけ更新するようにしたい
- ちゃんとスケールする書き方
- ちゃんとfcloseとか後始末する感じの書き方