13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CURLOPT_MAXCONNECTS / CURLMOPT_MAXCONNECTS / CURLMOPT_MAX_TOTAL_CONNECTIONS の違い

Last updated at Posted at 2016-08-18

概要

早見表

定数 意味
CURLOPT_MAXCONNECTS
(PHP5.5以降)
curl_execでキャッシュされる
非アクティブなTCPコネクション最大数
CURLMOPT_MAXCONNECTS
(PHP5.5以降)
curl_multi_execでキャッシュされる
非アクティブなTCPコネクション最大数
CURLMOPT_MAX_TOTAL_CONNECTIONS
(PHP7.0.7以降)
curl_multi_execで使用される**
アクティブな**TCPコネクション最大数
  • 非アクティブ
    → これをいじったところで何も嬉しくない場合がほとんど
  • アクティブ
    → これの数を制御したい!!!

従来は…

TCPコネクションを増やし過ぎないようにPHP側でプールサイズを制御する必要があった

  • 実装が面倒
  • この手法はCURLMOPT_PIPELINING によるHTTP/1.1のパイプライン化・HTTP/2のマルチプレクサの設定と相性が最悪
    • 実際にこれらのグループ化が行われたかどうかをPHP側から知るすべが無い

HTTP/1.1のパイプライン化を使う場合の問題

  • 確立直後のTCPコネクションではパイプライン化を行ってはならない
    • TCPコネクションがKeep-Aliveであることを確認してから初めて許可される

これはつらい.直接パイプライン化適用の有無を知る手段が無いため,いま当該サーバとの間のTCPコネクションが生きているかどうかを何とかして確認する必要があるが…実装は複雑になりそう.

HTTP/2のマルチプレクサを使う場合の問題

  • 相手がHTTP/2に対応しているかを事前に知っておかなければならない
    • HTTP/1.1にフォールバックされる可能性がある

こっちは相手サーバが限定される場合はまだ何とかなりそう…?

今後は…

PHP7.0.7以降はプールサイズの管理をlibcurlに丸投げできて最高

コードで理解する CURLMOPT_MAX_TOTAL_CONNECTIONS

コンストラクタでプールサイズを指定して,こんなコードを動かせるようにPoolクラスを作りたい.

$pool = new Pool(5);
for ($i = 0; $i < 10; ++$i) {
    $ch = curl_init('example.com');
    $pool->add($ch);
}
$pool->exec();
PHP7.0.6以前
class Pool
{
    protected $mh;
    protected $size;
    protected $added = [];
    protected $queue = [];

    public function __construct(int $size)
    {
        $this->mh = curl_multi_init();
        $this->size = $size;
    }

    public function add($ch)
    {
        if (count($this->added) < $this->size) {
            curl_multi_add_handle($this->mh, $ch);
            $this->added[(int)$ch] = $ch;
        } else {
            $this->queue[] = $ch;
        }
    }

    public function exec()
    {
        curl_multi_exec($this->mh, $running);
        do {
            if (curl_multi_select($this->mh) === -1) {
                usleep(2500);
                continue;
            }
            curl_multi_exec($this->mh, $running);
            $this->read();
        } while ($this->shift() || $running);
    }

    protected function read()
    {
        while ($info = curl_multi_info_read($this->mh)) {
            unset($this->added[(int)$info['handle']]);
        }
    }

    protected function shift()
    {
        while (count($this->added) < $size) {
            $ch = array_shift($this->queue);
            curl_multi_add_handle($this->mh, $ch);
            $this->added[(int)$ch] = $ch;
        }
        return isset($ch);
    }
}

オエエエエエエエエエ

PHP7.0.7以降
class Pool
{
    protected $mh;

    public function __construct(int $size)
    {
        $this->mh = curl_multi_init();
        curl_multi_setopt($this->mh, CURLMOPT_MAX_TOTAL_CONNECTIONS, $size);
        curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, 3);
    }

    public function add($ch)
    {
        curl_multi_add_handle($this->mh, $ch);
    }

    public function exec()
    {
        curl_multi_exec($this->mh, $running);
        do {
            if (curl_multi_select($this->mh) === -1) {
                usleep(2500);
                continue;
            }
            curl_multi_exec($this->mh, $running);
        } while ($running);
    }
}

イケメン!!!!しかもパイプラインやマルチプレクサも軽々扱えるなんて完璧すぎる!!!

補足

curl_multi_init で作成したリソースを破棄すると**「パイプラインが可能である」と許可しているメモが吹っ飛ぶ.**要注意.

13
14
3

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
13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?