Fla*_*ame 10 php queue background-process laravel guzzle
我有在多个队列工作器上运行的作业,其中包含一些使用 Guzzle 的 HTTP 请求。但是,GuzzleHttp\Exception\RequestException当我在后台进程中运行这些作业时,该作业中的 try-catch 块似乎没有启动。正在运行的进程是一个php artisan queue:workLaravel 队列系统工作线程,它监视队列并接收作业。
相反,抛出的异常是以下GuzzleHttp\Promise\RejectionException消息之一:
承诺被拒绝的原因:cURL 错误 28:操作在 30001 毫秒后超时,收到 0 个字节(参见 https://curl.haxx.se/libcurl/c/libcurl-errors.html)
这实际上是一种伪装GuzzleHttp\Exception\ConnectException(参见https://github.com/guzzle/promises/blob/master/src/RejectionException.php#L22),因为如果我在通过访问URL,我确实收到ConnectException了消息:
cURL 错误 28:操作在 100 毫秒后超时,收到 0 个字节中的 0 个(请参阅 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.
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在工作进程中运行时抛出 aRejectionException或ConnectException,但ConnectException在通过浏览器手动测试时总是抛出(据我所知)。
所以基本上我得出的结论是,这RejectionException是包装来自 的消息ConnectException,但是我没有使用 Guzzle 的异步功能。我的请求只是按顺序完成。唯一不同的是,多个 PHP 进程可能正在进行 Guzzle HTTP 调用,或者作业本身超时(这应该导致 Laravel 的不同异常Illuminate\Queue\MaxAttemptsExceededException),但我不明白这如何导致代码行为不同。
我在 Guzzle 包中找不到任何代码,这些代码在从 CLI 运行而不是浏览器触发器时使用php_sapi_name()/ PHP_SAPI(确定使用的接口)来执行不同的内容。
为什么 GuzzleRejectionException在我的工作进程上抛出了 me ,而ConnectException在通过浏览器触发的常规 PHP 脚本上抛出了s ?
遗憾的是,我无法创建一个最小的可重现示例。我在 Sentry 问题跟踪器中看到许多错误消息,上面显示了确切的异常。源被声明为Starting Artisan command: horizon:work(这是 Laravel Horizon,它监督 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
Run Code Online (Sandbox Code Playgroud)
cURL 7.58.0.guzzlehttp/guzzle 6.5.2laravel/framework 6.12.0 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
Run Code Online (Sandbox Code Playgroud)
该Client::callRequest()函数仅包含一个我调用的 Guzzle 客户端$client->request($request['method'], $request['url'], $request['options']);(所以我没有使用requestAsync())。我认为这与导致此问题的并行运行作业有关。
考虑以下发出 HTTP 请求的测试用例(应该返回常规的 200 响应):
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
Run Code Online (Sandbox Code Playgroud)
现在我最初做的是调用rejection_for($e->getMessage()),它RejectionException根据消息字符串创建自己的。rejection_for($e)在这里调用是正确的解决方案。唯一需要回答的是这个rejection_for函数是否与一个简单的throw $e.
您好,我想知道您是否遇到错误 4xx 或错误 5xx
\n\n但即便如此,我也会为与您的问题类似的解决方案提供一些替代方案
\n\n替代方案1
\n\n我想解决这个问题,与按预期工作的开发和测试环境相比,我遇到了新的生产服务器返回意外 400 响应的问题;只需安装 apt install php7.0-curl 即可修复它。
\n\n这是一个全新的 Ubuntu 16.04 LTS 安装,通过 ppa:ondrej/php 安装了 php,在调试过程中我注意到标头不同。两者都发送包含数据的多部分表单,但是如果没有 php7.0-curl,它会发送 Connection: close 标头而不是 Expect: 100-Continue;这两个请求都有 Transfer-Encoding: chunked。
\n\n\xc2\xa0替代方案 2
\n\n也许你应该尝试这个
\n\ntry {\n$client = new Client();\n$guzzleResult = $client->put($url, [\n 'body' => $postString\n]);\n} catch (\\GuzzleHttp\\Exception\\RequestException $e) {\n$guzzleResult = $e->getResponse();\n}\n\nvar_export($guzzleResult->getStatusCode());\nvar_export($guzzleResult->getBody());\nRun Code Online (Sandbox Code Playgroud)\n\n如果响应代码不是 200,Guzzle 需要捕获
\n\n替代方案3
\n\n就我而言,这是因为我在请求的 $options['json'] 中传递了一个空数组\n即使传递 Content-Type: application/json 请求标头,我也无法使用 Postman 或 cURL 在服务器上重现 500 。
\n\n无论如何,从请求的选项数组中删除 json 键解决了问题。
\n\n我花了大约 30 分钟试图找出问题所在,因为这种行为非常不一致。对于我提出的所有其他请求,传递 $options['json'] = [] 不会导致任何问题。这可能是服务器问题,我无法控制服务器。
\n\n发送有关获得的详细信息的反馈
\n