我正在使用Guzzle将大量请求发送到API端点,并使用Pool
功能来异步并发发送这些请求。
脚本如下:
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;
/* Configure logger */
Logger::configure("config/logger.xml");
$logger = Logger::getLogger("console");
/* Configure Guzzle Client */
$client = new Client([
'base_uri' => 'http://my.api/',
'timeout' => 2.0,
'allow_redirects' => false,
]);
/* Anonymous function (closure) to 'yield' X number of Requests */
$requests = function ($num_requests) {
for ($i = 0; $i < $num_requests; $i++) {
yield new Request('GET', "/MY_UNIQUE_IDENTIFIER/");
}
};
/* Create a Pool for the above requests */
$pool = new Pool($client, $requests(20), [
'concurrency' => 5, // Determine how many requests to send concurrently
'fulfilled' => function ($response, $index) {
$logger->info('$index: ' . $index . ', $response: ' . $response);
},
'rejected' => function ($reason, $index) {
try {
echo $reason;
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_WARNING);
}
},
]);
/* Initiate transfers/create a promise */
$promise = $pool->promise();
/* Force the pool of requests to complete */
$promise->wait();
基本上,向http://my.api/MY_UNIQUE_IDENTIFIER
发送20个请求(一次5个)。
Pool
似乎可以正常工作。如果将echo
添加到rejected
请求中,则输出如下:
#0 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php(149): GuzzleHttp\Handler\CurlFactory::createRejection(Object(GuzzleHttp\Handler\EasyHandle), Array)
#1 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php(102): GuzzleHttp\Handler\CurlFactory::finishError(Object(GuzzleHttp\Handler\CurlMultiHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#2 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(181): GuzzleHttp\Handler\CurlFactory::finish(Object(GuzzleHttp\Handler\CurlMultiHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#3 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(110): GuzzleHttp\Handler\CurlMultiHandler->processMessages()
#4 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(125): GuzzleHttp\Handler\CurlMultiHandler->tick()
#5 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(246): GuzzleHttp\Handler\CurlMultiHandler->execute(true)
#6 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(223): GuzzleHttp\Promise\Promise->invokeWaitFn()
#7 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(267): GuzzleHttp\Promise\Promise->waitIfPending()
#8 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(225): GuzzleHttp\Promise\Promise->invokeWaitList()
#9 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(62): GuzzleHttp\Promise\Promise->waitIfPending()
#10 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/EachPromise.php(101): GuzzleHttp\Promise\Promise->wait()
#11 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(246): GuzzleHttp\Promise\EachPromise->GuzzleHttp\Promise\{closure}(true)
#12 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(223): GuzzleHttp\Promise\Promise->invokeWaitFn()
#13 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(62): GuzzleHttp\Promise\Promise->waitIfPending()
#14 /Users/me/guzzle-POC/poc.php(50): GuzzleHttp\Promise\Promise->wait()
#15 {main}GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve host: my.api (see http://curl.haxx.se/libcurl/c/libcurl-errors.html) in /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:185
这里的主要问题是#15
,Could not resolve host: my.api
。这是预期的行为,但是我想捕获此异常。
我使用的try/catch
根本不起作用-它什么也没抓住。
大概是由于Pool的异步特性,但是有可能以任何方式捕获这些异常吗?
我基本上想要实现的是if can't resolve; log error and continue with other requests
型方法。
答案 0 :(得分:2)
我遇到了类似的问题,我会分享一个对我有用的解决方案。
以你的问题为例,
这张图给出了 guzzle 异常的树层次结构(来源 Guzzle Docs。)
还有 GuzzleHttp\Exception\ConnectException
异常会在发生网络错误时抛出,而且 ConnectException
没有关联的响应,因此没有 400 或 500 错误。所以它应该为 hasResponse()
$client = new Client([
'base_uri' => 'http://my.api/',
'timeout' => 2.0,
'allow_redirects' => false,
]);
/* Anonymous function (closure) to 'yield' X number of Requests */
$requests = function ($num_requests) {
for ($i = 0; $i < $num_requests; $i++) {
yield new Request('GET', "/MY_UNIQUE_IDENTIFIER/");
}
};
/* Create a Pool for the above requests */
$pool = new Pool($client, $requests(20), [
'concurrency' => 5, // Determine how many requests to send concurrently
'fulfilled' => function ($response, $index) {
$logger->info('$index: ' . $index . ', $response: ' . $response);
},
'rejected' => function (\GuzzleHttp\Exception\TransferException $reason, $index) {
if ($reason->hasResponse()){
// this will mainly catch RequestException(Exception with statuscode and responses)
if ($reason->getResponse()->getStatusCode() == '400') {
// log your exception
} elseif($reason->getResponse()->getStatusCode() == '403'){
//log your unauthorised code exception
}else{
//similarly log your other status code exception
}
} else{
// ConnectException should come here, you can log it, will not have any responses as the request was never sent.
}
},
]);
/* Initiate transfers/create a promise */
$promise = $pool->promise();
/* Force the pool of requests to complete */
$promise->wait();
您需要知道异常的确切状态代码。
此外,如果有人专门只捕获 ServerException 或 ClientException,那么他们可以用层次树中的其他异常替换 TransferException。
'rejected' => function (\GuzzleHttp\Exception\RequestException $reason, $index) {