gpt4 book ai didi

php - Guzzle 在后台进程中抛出 RejectionException 而不是 ConnectionException

转载 作者:行者123 更新时间:2023-12-03 11:21:19 26 4
gpt4 key购买 nike

我有在多个队列工作人员上运行的作业,其中包含一些使用 Guzzle 的 HTTP 请求。但是,此作业中的 try-catch block 似乎无法接收 GuzzleHttp\Exception\RequestException当我在后台进程中运行这些作业时。正在运行的进程是php artisan queue:work这是一个 Laravel 队列系统 worker ,它监控队列并获取作业。

相反,抛出的异常是 GuzzleHttp\Promise\RejectionException 之一。留言:

The promise was rejected with reason: cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)



这其实是一个变相的 GuzzleHttp\Exception\ConnectException (见 https://github.com/guzzle/promises/blob/master/src/RejectionException.php#L22 ),因为如果我在通过访问 URL 触发的常规 PHP 进程中运行类似的作业,我会得到 ConnectException按照消息的预期:

cURL error 28: Operation timed out after 100 milliseconds with 0 out of 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)



将触发此超时的示例代码:

try {
$c = new \GuzzleHttp\Client([
'timeout' => 0.1
]);
$response = (string) $c->get('https://example.com')->getBody();
} catch(GuzzleHttp\Exception\RequestException $e) {
// This occasionally gets catched when a ConnectException (child) is thrown,
// but it doesnt happen with RejectionException because it is not a child
// of RequestException.
}

上面的代码会抛出 RejectionExceptionConnectException在工作进程中运行时,但始终为 ConnectException当通过浏览器手动测试时(据我所知)。

所以基本上我得出的是这个 RejectionException正在包装来自 ConnectException 的消息,但是我没有使用 Guzzle 的异步功能。我的请求只是按顺序完成的。唯一不同的是,多个 PHP 进程可能正在进行 Guzzle HTTP 调用,或者作业本身正在超时(这应该导致 Laravel 的 Illuminate\Queue\MaxAttemptsExceededException 出现不同的异常),但我不明白这是如何导致代码运行的不同。

我在 Guzzle 包中找不到任何使用 php_sapi_name() 的代码/ PHP_SAPI (确定使用的接口(interface))从 CLI 运行时执行不同的东西,而不是浏览器触发器。

tl;博士

为什么 Guzzle 会扔我 RejectionException s 在我的工作进程上,但是 ConnectException s 在通过浏览器触发的常规 PHP 脚本上?

编辑 1

可悲的是,我无法创建一个最小的可重现示例。我在我的 Sentry 问题跟踪器中看到了许多错误消息,上面显示了确切的异常。来源声明为 Starting Artisan command: horizon:work (这是 Laravel Horizo​​n,它监督 Laravel 队列)。我再次检查了 PHP 版本之间是否存在差异,但网站和工作进程都运行相同的 PHP 7.3.14哪个是对的:
PHP 7.3.14-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Jan 23 2020 13:59:16) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.14, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.14-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
  • cURL 版本为 cURL 7.58.0 .
  • Guzzle 版本是 guzzlehttp/guzzle 6.5.2
  • Laravel 版本是 laravel/framework 6.12.0

  • 编辑 2(堆栈跟踪)
        GuzzleHttp\Promise\RejectionException: The promise was rejected with reason: cURL error 28: Operation timed out after 30000 milliseconds with 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
    #44 /vendor/guzzlehttp/promises/src/functions.php(112): GuzzleHttp\Promise\exception_for
    #43 /vendor/guzzlehttp/promises/src/Promise.php(75): GuzzleHttp\Promise\Promise::wait
    #42 /vendor/guzzlehttp/guzzle/src/Client.php(183): GuzzleHttp\Client::request
    #41 /app/Bumpers/Client.php(333): App\Bumpers\Client::callRequest
    #40 /app/Bumpers/Client.php(291): App\Bumpers\Client::callFunction
    #39 /app/Bumpers/Client.php(232): App\Bumpers\Client::bumpThread
    #38 /app/Models/Bumper.php(206): App\Models\Bumper::post
    #37 /app/Jobs/PostBumper.php(59): App\Jobs\PostBumper::handle
    #36 [internal](0): call_user_func_array
    #35 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}
    #34 /vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\Container\Util::unwrapIfClosure
    #33 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\Container\BoundMethod::callBoundMethod
    #32 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\Container\BoundMethod::call
    #31 /vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\Container\Container::call
    #30 /vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(94): Illuminate\Bus\Dispatcher::Illuminate\Bus\{closure}
    #29 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(130): Illuminate\Pipeline\Pipeline::Illuminate\Pipeline\{closure}
    #28 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(105): Illuminate\Pipeline\Pipeline::then
    #27 /vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(98): Illuminate\Bus\Dispatcher::dispatchNow
    #26 /vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(83): Illuminate\Queue\CallQueuedHandler::Illuminate\Queue\{closure}
    #25 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(130): Illuminate\Pipeline\Pipeline::Illuminate\Pipeline\{closure}
    #24 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(105): Illuminate\Pipeline\Pipeline::then
    #23 /vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(85): Illuminate\Queue\CallQueuedHandler::dispatchThroughMiddleware
    #22 /vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(59): Illuminate\Queue\CallQueuedHandler::call
    #21 /vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(88): Illuminate\Queue\Jobs\Job::fire
    #20 /vendor/laravel/framework/src/Illuminate/Queue/Worker.php(354): Illuminate\Queue\Worker::process
    #19 /vendor/laravel/framework/src/Illuminate/Queue/Worker.php(300): Illuminate\Queue\Worker::runJob
    #18 /vendor/laravel/framework/src/Illuminate/Queue/Worker.php(134): Illuminate\Queue\Worker::daemon
    #17 /vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(112): Illuminate\Queue\Console\WorkCommand::runWorker
    #16 /vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(96): Illuminate\Queue\Console\WorkCommand::handle
    #15 /vendor/laravel/horizon/src/Console/WorkCommand.php(46): Laravel\Horizon\Console\WorkCommand::handle
    #14 [internal](0): call_user_func_array
    #13 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}
    #12 /vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\Container\Util::unwrapIfClosure
    #11 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\Container\BoundMethod::callBoundMethod
    #10 /vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\Container\BoundMethod::call
    #9 /vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\Container\Container::call
    #8 /vendor/laravel/framework/src/Illuminate/Console/Command.php(201): Illuminate\Console\Command::execute
    #7 /vendor/symfony/console/Command/Command.php(255): Symfony\Component\Console\Command\Command::run
    #6 /vendor/laravel/framework/src/Illuminate/Console/Command.php(188): Illuminate\Console\Command::run
    #5 /vendor/symfony/console/Application.php(1012): Symfony\Component\Console\Application::doRunCommand
    #4 /vendor/symfony/console/Application.php(272): Symfony\Component\Console\Application::doRun
    #3 /vendor/symfony/console/Application.php(148): Symfony\Component\Console\Application::run
    #2 /vendor/laravel/framework/src/Illuminate/Console/Application.php(93): Illuminate\Console\Application::run
    #1 /vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(131): Illuminate\Foundation\Console\Kernel::handle
    #0 /artisan(37): null
    Client::callRequest()函数只包含一个 Guzzle 客户端,我在其上调用 $client->request($request['method'], $request['url'], $request['options']); (所以我不使用 requestAsync() )。我认为这与导致此问题的并行运行作业有关。

    编辑 3(找到解决方案)

    考虑以下发出 HTTP 请求(应返回常规 200 响应)的测试用例:

            try {
    $c = new \GuzzleHttp\Client([
    'base_uri' => 'https://example.com'
    ]);
    $handler = $c->getConfig('handler');
    $handler->push(\GuzzleHttp\Middleware::mapResponse(function(ResponseInterface $response) {
    // Create a fake connection exception:
    $e = new \GuzzleHttp\Exception\ConnectException('abc', new \GuzzleHttp\Psr7\Request('GET', 'https://example.com/2'));

    // These 2 lines both cascade as `ConnectException`:
    throw $e;
    return \GuzzleHttp\Promise\rejection_for($e);

    // This line cascades as a `RejectionException`:
    return \GuzzleHttp\Promise\rejection_for($e->getMessage());
    }));
    $c->get('');
    } catch(\Exception $e) {
    var_dump($e);
    }

    现在我最初所做的是调用 rejection_for($e->getMessage())创建自己的 RejectionException基于消息字符串。调用 rejection_for($e)是这里的正确解决方案。唯一需要回答的是,如果这个 rejection_for功能与简单的 throw $e 相同.

    最佳答案

    您好,我想知道您是遇到错误 4xx 还是错误 5xx

    但即便如此,我也会为与您的问题相似的解决方案提供一些替代方案

    备选方案 1

    我想解决这个问题,与按预期工作的开发和测试环境相比,新生产服务器返回意外的 400 响应时遇到了这个问题;只需安装 apt install php7.0-curl 即可修复它。

    这是一个全新的 Ubuntu 16.04 LTS 安装,通过 ppa:ondrej/php 安装了 php,在调试过程中我注意到标题不同。两者都发送了一个带有卡盘数据的多部分表单,但是没有 php7.0-curl 它发送的是 Connection: close header 而不是 Expect: 100-Continue;这两个请求都有 Transfer-Encoding: 分 block 。

    备选方案 2

    也许你应该试试这个

    try {
    $client = new Client();
    $guzzleResult = $client->put($url, [
    'body' => $postString
    ]);
    } catch (\GuzzleHttp\Exception\RequestException $e) {
    $guzzleResult = $e->getResponse();
    }

    var_export($guzzleResult->getStatusCode());
    var_export($guzzleResult->getBody());

    如果响应代码不是 200,Guzzle 需要捕获

    备选方案 3

    就我而言,这是因为我在请求的 $options['json'] 中传递了一个空数组
    即使传递 Content-Type: application/json 请求 header ,我也无法使用 Postman 或 cURL 在服务器上重现 500。

    无论如何,从请求的选项数组中删除 json 键解决了这个问题。

    我花了大约 30 分钟试图找出问题所在,因为这种行为非常不一致。对于我提出的所有其他请求,传递 $options['json'] = [] 不会引起任何问题。可能是服务器问题,我不控制服务器。

    发送关于获得的详细信息的反馈

    关于php - Guzzle 在后台进程中抛出 RejectionException 而不是 ConnectionException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60211758/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com