处理请求池中的异常

时间:2019-05-17 09:34:14

标签: php guzzle

我正在使用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

这里的主要问题是#15Could not resolve host: my.api。这是预期的行为,但是我想捕获此异常

我使用的try/catch根本不起作用-它什么也没抓住。

大概是由于Pool的异步特性,但是有可能以任何方式捕获这些异常吗?

我基本上想要实现的是if can't resolve; log error and continue with other requests型方法。

1 个答案:

答案 0 :(得分:2)

我遇到了类似的问题,我会分享一个对我有用的解决方案。

以你的问题为例,

这张图给出了 guzzle 异常的树层次结构(来源 Guzzle Docs。) Source: https://docs.guzzlephp.org/en/latest/quickstart.html#exceptions

还有 GuzzleHttp\Exception\ConnectException 异常会在发生网络错误时抛出,而且 ConnectException 没有关联的响应,因此没有 400 或 500 错误。所以它应该为 hasResponse()

提供 false
$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) {
相关问题