Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
547 views
in Technique[技术] by (71.8m points)

php - Handling exceptions on a Pool of Requests

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 GuzzleHttpClient;
use GuzzleHttpPromise;
use GuzzleHttpPool;
use GuzzleHttpPsr7Request;
use GuzzleHttpExceptionRequestException;

/* 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): GuzzleHttpHandlerCurlFactory::createRejection(Object(GuzzleHttpHandlerEasyHandle), Array)
#1 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php(102): GuzzleHttpHandlerCurlFactory::finishError(Object(GuzzleHttpHandlerCurlMultiHandler), Object(GuzzleHttpHandlerEasyHandle), Object(GuzzleHttpHandlerCurlFactory))
#2 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(181): GuzzleHttpHandlerCurlFactory::finish(Object(GuzzleHttpHandlerCurlMultiHandler), Object(GuzzleHttpHandlerEasyHandle), Object(GuzzleHttpHandlerCurlFactory))
#3 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(110): GuzzleHttpHandlerCurlMultiHandler->processMessages()
#4 /Users/me/guzzle-POC/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php(125): GuzzleHttpHandlerCurlMultiHandler->tick()
#5 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(246): GuzzleHttpHandlerCurlMultiHandler->execute(true)
#6 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(223): GuzzleHttpPromisePromise->invokeWaitFn()
#7 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(267): GuzzleHttpPromisePromise->waitIfPending()
#8 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(225): GuzzleHttpPromisePromise->invokeWaitList()
#9 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(62): GuzzleHttpPromisePromise->waitIfPending()
#10 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/EachPromise.php(101): GuzzleHttpPromisePromise->wait()
#11 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(246): GuzzleHttpPromiseEachPromise->GuzzleHttpPromise{closure}(true)
#12 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(223): GuzzleHttpPromisePromise->invokeWaitFn()
#13 /Users/me/guzzle-POC/vendor/guzzlehttp/promises/src/Promise.php(62): GuzzleHttpPromisePromise->waitIfPending()
#14 /Users/me/guzzle-POC/poc.php(50): GuzzleHttpPromisePromise->wait()
#15 {main}GuzzleHttpExceptionConnectException: 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.) Source: https://docs.guzzlephp.org/en/latest/quickstart.html#exceptions

Also GuzzleHttpExceptionConnectException 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 (GuzzleHttpExceptionTransferException $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 (GuzzleHttpExceptionRequestException $reason, $index) {

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...