问题
I'm using Guzzle to send a number of requests to an API endpoint, using the Pool
functionality to send these asynchronously and concurrently.
The script looks like this:
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();
Basically, send 20 requests (5 at a time) to http://my.api/MY_UNIQUE_IDENTIFIER
.
The Pool
appears to work. If I add an echo
to the rejected
requests, I get output like:
#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
The main problem here being #15
, Could not resolve host: my.api
. This is expected behaviour, but I want to catch this exception.
The try/catch
that I've used just doesn't work at all - it catches nothing.
Presumably this is because of the Pool's asynchronous nature, but is it possible to catch these exceptions in any way?
All I want to achieve basically is, if can't resolve; log error and continue with other requests
-type approach.
回答1:
I had a similar problem, I would share a solution which worked for me.
Taking example from your question,
This is the picture which gives the tree heirarchy of guzzle exception(source Guzzle Docs.)
Also GuzzleHttp\Exception\ConnectException
exception is thrown in the event of a networking error and also ConnectException
does not have an associated response, so therefore there is no 400 or 500 error. So it should give false for 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();
Demerits
You need to know the exact status code of your exceptions.
Also if someone is specific to only catch ServerException or ClientException, then they can replace TransferException with other exception down the heirarchy tree.
'rejected' => function (\GuzzleHttp\Exception\RequestException $reason, $index) {
来源:https://stackoverflow.com/questions/56183592/handling-exceptions-on-a-pool-of-requests