Help us understand the problem. What is going on with this article?

curl_multiでHTTP並行リクエストを行うサンプル

More than 3 years have passed since last update.

curl_multiはselectシステムコールを使って同時に複数のHTTPリクエストを行うことができます。マルチスレッドやらマルチプロセスを使っているわけではなく、原理はnode.jsなんかと近いI/O多重化で、一度に一つのことしかしていません。

PHPのcurlはlibcurlのAPIをほぼ踏襲しており、ちょっと取っつきにくいです。クラスでラッピングして、curl_close()などはデストラクタで呼ばれるように自動化すると、もう少しすっきりすると思います。

curl_multi.php
<?php
/**
 * curl_multiでHTTP複数リクエストを並列実行するテンプレ
 *
 */

//タイムアウト時間を決めておく
$TIMEOUT = 10; //10秒

/*
 * 1) 準備
 *  - curl_multiハンドラを用意
 *  - 各リクエストに対応するcurlハンドラを用意
 *    リクエスト分だけ必要
 *    * レスポンスが必要な場合はRETURNTRANSFERオプションをtrueにしておくこと。
 *  - 全てcurl_multiハンドラに追加
 */
$mh = curl_multi_init();

$urls = array(
    'http://localhost/sleep.php?wait=3',
    'http://localhost/sleep.php?wait=2',
    'http://localhost/sleep.php?wait=1',
);
foreach ($urls as $u) {
    $ch = curl_init();
    curl_setopt_array($ch, array(
        CURLOPT_URL            => $u,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => $TIMEOUT,
        CURLOPT_CONNECTTIMEOUT => $TIMEOUT,
    ));
    curl_multi_add_handle($mh, $ch);
}


/*
 * 2) リクエストを開始する
 *  - curl_multiでは即座に制御が戻る(レスポンスが返ってくるのを待たない)
 *  - いきなり失敗するケースを考えてエラー処理を書いておく
 *  - do~whileはlibcurl<7.20で必要
 */
do {
    $stat = curl_multi_exec($mh, $running); //multiリクエストスタート
} while ($stat === CURLM_CALL_MULTI_PERFORM);
if ( ! $running || $stat !== CURLM_OK) {
    throw new RuntimeException('リクエストが開始出来なかった。マルチリクエスト内のどれか、URLの設定がおかしいのでは?');
}

/*
 * 3) レスポンスをcurl_multi_selectで待つ
 *  - 何かイベントがあったらループが進む
 *    selectはイベントが起きるまでCPUをほとんど消費せずsleep状態になる
 *  - どれか一つレスポンスが返ってきたらselectがsleepを中断して何か数字を返す。
 *
 */
do switch (curl_multi_select($mh, $TIMEOUT)) { //イベントが発生するまでブロック
    // 最悪$TIMEOUT秒待ち続ける。
    // あえて早めにtimeoutさせると、レスポンスを待った状態のまま別の処理を挟めるようになります。
    // もう一度curl_multi_selectを繰り返すと、またイベントがあるまでブロックして待ちます。

    case -1: //selectに失敗。ありうるらしい。 https://bugs.php.net/bug.php?id=61141
        usleep(10); //ちょっと待ってからretry。ここも別の処理を挟んでもよい。
        do {
            $stat = curl_multi_exec($mh, $running);
        } while ($stat === CURLM_CALL_MULTI_PERFORM);
        continue 2;

    case 0:  //タイムアウト -> 必要に応じてエラー処理に入るべきかも。
        continue 2; //ここではcontinueでリトライします。

    default: //どれかが成功 or 失敗した
        do {
            $stat = curl_multi_exec($mh, $running); //ステータスを更新
        } while ($stat === CURLM_CALL_MULTI_PERFORM);

        do if ($raised = curl_multi_info_read($mh, $remains)) {
            //変化のあったcurlハンドラを取得する
            $info = curl_getinfo($raised['handle']);
            echo "$info[url]: $info[http_code]\n";
            $response = curl_multi_getcontent($raised['handle']);

            if ($response === false) {
                //エラー。404などが返ってきている
                echo 'ERROR!!!', PHP_EOL;
            } else {
                //正常にレスポンス取得
                echo $response, PHP_EOL;
            }
            curl_multi_remove_handle($mh, $raised['handle']);
            curl_close($raised['handle']);
        } while ($remains);
        //select前に全ての処理が終わっていたりすると
        //複数の結果が入っていることがあるのでループが必要

} while ($running);
echo 'finished', PHP_EOL;
curl_multi_close($mh);

レスポンスに1秒、2秒、3秒かかる3つのAPIにリクエストしていますが、合計3秒程度で処理が完了します。

http://techblog.yahoo.co.jp/architecture/api1_curl_multi/
この記事とか、selectを使わず無限ループでレスポンスを待っている例が散見されるけど、待っている間CPUを食い潰してとても非効率なので、必ずselectを使いましょう。

サンプルでリクエストしているAPIは↓こんなの。waitに指定した秒数だけ待ってレスポンスを返します。

sampleAPI.php
<?php
/*
 * セキュリティ的に問題のあるスクリプトなので実験以外では使わないでね。
 */
sleep((int)$_GET['wait']);
header('Content-Type: text/plain');
echo $_GET['wait'];
Hiraku
PHP, Go界隈をうろうろしています。最近はgRPCと戦ってる。 特に明示していなければ、記事中のソースコード片は `CC-0 1.0` とします。出典表示無しで自由にコピペして頂いて構いません。 ただ、記事自体をコピペされるのは嫌なので、ソースコード部分以外の文章は通常通り全ての著作権を私が保持するものとします。 引用を超える範囲のコピペは止めて下さい。
http://blog.tojiru.net/
mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。
https://tech.mercari.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away