Guzzle kullanarak bazı HTTP istekleri içeren birden çok kuyruk çalışanı üzerinde çalışan işlerim var. Ancak, GuzzleHttp\Exception\RequestException
bu işi arka plan işleminde çalıştırdığımda bu iş içindeki try-catch bloğu görünmüyor . Çalışan işlem, php artisan queue:work
kuyruğu izleyen ve işleri alan bir Laravel kuyruk sistemi çalışanıdır.
Bunun yerine, atılan istisna şu GuzzleHttp\Promise\RejectionException
iletiden biridir:
Vaat nedeni ile reddedildi: cURL hatası 28: İşlem 0 bayt ile 30001 milisaniyeden sonra zaman aşımına uğradı (bkz. Https://curl.haxx.se/libcurl/c/libcurl-errors.html )
Bu aslında gizlenmiş GuzzleHttp\Exception\ConnectException
(bkz. Https://github.com/guzzle/promises/blob/master/src/RejectionException.php#L22 ), çünkü benzer bir işi ziyaret ederek tetiklenen düzenli bir PHP işleminde çalıştırırsam URL, ben ConnectException
mesaj ile amaçlandığı gibi olsun :
cURL hatası 28: 0 baytın 0'ının 0'ı alınarak 100 milisaniyeden sonra zaman aşımına uğradı (bkz. https://curl.haxx.se/libcurl/c/libcurl-errors.html )
Bu zaman aşımını tetikleyecek örnek kod:
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.
}
Yukarıdaki kod ya çalışan işlemde RejectionException
ya ConnectException
da çalıştırıldığında atar , ancak her ConnectException
zaman tarayıcı aracılığıyla manuel olarak test edildiğinde (anlatabileceğimden).
Temelde elde ettiğim şey, bunun RejectionException
mesajı kaydırmasıdır ConnectException
, ancak Guzzle'ın asenkron özelliklerini kullanmıyorum. İsteklerim basitçe seri olarak yapılır. Farklı olan tek şey birden fazla PHP işleminin Guzzle HTTP çağrıları yapması veya işlerin kendisinin zaman aşımına uğraması (ki bu da Laravel'in farklı bir istisnasına neden olması Illuminate\Queue\MaxAttemptsExceededException
), ancak bunun kodun farklı davranmasına neden olduğunu görmüyorum.
CLI'den bir tarayıcı tetikleyicisinin aksine çalıştırırken farklı şeyler yürütmek için kullanılan php_sapi_name()
/ PHP_SAPI
(kullanılan arabirimi belirler) Guzzle paketleri içinde herhangi bir kod bulamadım .
tl; Dr.
Neden Guzzle beni alt RejectionException
süreçlerime atıyor , ancak ConnectException
tarayıcı aracılığıyla tetiklenen düzenli PHP komut dosyalarına atıyor ?
Düzenle 1
Ne yazık ki minimal tekrarlanabilir bir örnek oluşturamıyorum. Yukarıda belirtilen istisna dışında, Sentry sorun izleyicimde birçok hata mesajı görüyorum. Kaynak olarak belirtilir Starting Artisan command: horizon:work
(Laravel Horizon, Laravel kuyruklarını denetler). PHP sürümleri arasında bir tutarsızlık olup olmadığını görmek için tekrar kontrol ettim, ancak hem web sitesi hem de çalışan işlemler aynı PHP'yi çalıştırıyor 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 sürümü
cURL 7.58.0
. - Guzzle sürümü
guzzlehttp/guzzle 6.5.2
- Laravel sürümü
laravel/framework 6.12.0
Düzenleme 2 (yığın izlemesi)
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
Bu Client::callRequest()
fonksiyon sadece çağırdığım bir Guzzle İstemcisi içerir $client->request($request['method'], $request['url'], $request['options']);
(bu yüzden im kullanmıyorum requestAsync()
). Bu soruna neden olan işleri paralel olarak yürütmekle ilgili bir şey olduğunu düşünüyorum.
Edit 3 (çözüm bulundu)
Bir HTTP isteği yapan (normal 200 yanıt döndürmesi gereken) aşağıdaki test durumunu düşünün:
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);
}
Şimdi aslında yaptığım mesaj dizesine göre rejection_for($e->getMessage())
kendi oluşturur çağrı oldu RejectionException
. Arama rejection_for($e)
burada doğru çözümdü. Yanıtlanması gereken tek şey, bu rejection_for
fonksiyon bir basit ile aynı ise throw $e
.
HandlerStack
mı (ipucu:) ?