Guzzleでタイムアウトを1秒未満に設定したい時に必要となる記述を残しておきます。
前提条件
- Guzzle 6系
- PHP 5.6系
解決方法
curlのオプション CURLOPT_NOSIGNAL
を true
にします。
use GuzzleHttp\Client;
$request_params = ['hoge' => 'foo'];
$guzzle_client = new Client(['base_uri' => 'http://example.com/']);
$http_response = $guzzle_client->request(
'POST',
'/api',
[
'timeout' => 0.1, // 100msec
'curl' => [CURLOPT_NOSIGNAL => true], // これが要!
'json' => $request_params
]
);
var_dump($http_response->getBody());
背景説明
内部的には、Guzzle→PHP→libcurlという流れで実行されています。libcurlは(少なくとも)Linux環境において、ディストリビューションが提供する標準のものを利用すると、タイムアウトはカーネルが提供するシグナル SIGALRM
を使います。
しかし、 SIGALRM
は秒単位でしか設定できないことが、今回の問題のポイントです。ですので、1秒未満になると切り捨てられてまともに動きません。
そこで、libcurlにてシグナルを使わないよう設定をした上で、タイムアウトを設定すると、シグナルに頼らずタイムアウトを観測できるようになり、問題が解決します。
なお、Guzzleのタイムアウトの設定は CURLOPT_TIMEOUT
ではなく CURLOPT_TIMEOUT_MS
を使っていて、Guzzleとしては1秒未満の値を定義することはできます。ここ、私が勘違いしていたところでした。
謝辞
@do-aki@github さんに助言いただきました。どうもありがとうございました!
@koemu guzzle って、 CURLOPT_CONNECTTIMEOUT_MS 利用してません?
— どぅーあき (@do_aki) February 9, 2017