Guzzle การขว้างปา RejectionException แทน ConnectionException บนกระบวนการพื้นหลัง


9

ฉันมีงานที่ทำงานหลายคนที่มีการร้องขอ HTTP โดยใช้ Guzzle อย่างไรก็ตามบล็อก try-catch ภายในงานนี้ดูเหมือนจะไม่ได้รับGuzzleHttp\Exception\RequestExceptionเมื่อฉันกำลังเรียกใช้งานเหล่านี้ในกระบวนการพื้นหลัง กระบวนการกำลังทำงานอยู่php artisan queue:workซึ่งเป็นผู้ปฏิบัติงานระบบคิว Laravel ที่ตรวจสอบคิวและหยิบงาน

แต่ข้อยกเว้นที่ถูกโยนเป็นหนึ่ง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 ) เพราะถ้าฉันทำงานที่คล้ายกันในกระบวนการ PHP ปกติที่เรียกใช้โดยไปที่ 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.
}

รหัสข้างต้นพ่น a RejectionExceptionหรือConnectExceptionเมื่อทำงานในกระบวนการของผู้ปฏิบัติงาน แต่เสมอConnectExceptionเมื่อทดสอบด้วยตนเองผ่านเบราว์เซอร์ (จากสิ่งที่ฉันสามารถบอกได้)

ดังนั้นโดยทั่วไปสิ่งที่ฉันได้รับคือนี่RejectionExceptionคือการตัดข้อความจากConnectExceptionอย่างไรก็ตามฉันไม่ได้ใช้คุณสมบัติอะซิงโครนัสของ Guzzle คำขอของฉันเป็นแบบเรียบง่าย สิ่งเดียวที่แตกต่างคือกระบวนการ PHP หลายอย่างอาจเรียกใช้ Guzzle HTTP หรือว่างานนั้นหมดเวลา (ซึ่งควรส่งผลให้เกิดข้อยกเว้นที่แตกต่างกันของ Laravel Illuminate\Queue\MaxAttemptsExceededException) แต่ฉันไม่เห็นว่าสิ่งนี้ทำให้รหัสทำงานแตกต่างกันอย่างไร

ฉันไม่สามารถค้นหารหัสใด ๆ ภายในแพ็คเกจแพ็คเกจที่ใช้php_sapi_name()/ PHP_SAPI(ซึ่งกำหนดอินเทอร์เฟซที่ใช้) เพื่อดำเนินการสิ่งต่าง ๆ เมื่อเรียกใช้จาก CLI ซึ่งตรงข้ามกับทริกเกอร์ของเบราว์เซอร์

TL; DR

ทำไม Guzzle ทำให้ฉันตกRejectionExceptionอยู่ในกระบวนการทำงานของฉัน แต่ConnectExceptionสคริปต์ PHP ปกติทริกเกอร์ผ่านเบราว์เซอร์

แก้ไข 1

น่าเศร้าที่ฉันไม่สามารถสร้างตัวอย่างที่ทำซ้ำได้เพียงเล็กน้อย ฉันเห็นข้อความแสดงข้อผิดพลาดมากมายในตัวติดตามปัญหา 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
  • 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()ฟังก์ชั่นมีเพียงล่อไคลเอนต์ที่ผมเรียก$client->request($request['method'], $request['url'], $request['options']);(เพื่อ im ไม่ได้ใช้requestAsync()) ฉันคิดว่ามันมีบางอย่างเกี่ยวกับการรันงานแบบขนานที่ทำให้เกิดปัญหานี้

แก้ไข 3 (พบวิธีแก้ปัญหา)

พิจารณา testcase ต่อไปนี้ซึ่งสร้างคำร้องขอ 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_forthrow $e


คุณใช้ Guzzle เวอร์ชันใด
Vladimir

1
คุณใช้คิวไดรเวอร์รุ่นใดสำหรับ laravel มีพนักงานกี่คนที่ทำงานแบบขนานบนอินสแตนซ์ / ต่ออินสแตนซ์? คุณมีมิดเดิลแวร์ guzzle แบบกำหนดเองอยู่แล้ว (คำใบ้:) HandlerStack?
Christoph Kluge

คุณสามารถระบุสแต็กการติดตามจาก Sentry ได้หรือไม่?
Vladimir

@Vladimir ive เพิ่มการติดตามสแต็ก ฉันไม่คิดว่ามันจะช่วยคุณได้มากนัก วิธีการที่สัญญาถูกนำไปใช้ใน Guzzle (และโดยทั่วไป PHP) นั้นยากที่จะอ่าน
Flame

1
@Flame คุณสามารถแบ่งปันมิดเดิลแวร์ซึ่งดำเนินการตามคำขอย่อยได้หรือไม่ ฉันเดาว่าปัญหาจะอยู่ที่นั่น ในขณะเดียวกันฉันจะเพิ่มคำตอบที่ทำซ้ำได้กับวิทยานิพนธ์ของฉัน
Christoph Kluge

คำตอบ:


3

สวัสดีฉันอยากรู้ว่าคุณมีข้อผิดพลาด 4xx หรือข้อผิดพลาด 5xx

แต่ถึงกระนั้นฉันจะใส่ทางเลือกบางอย่างสำหรับการแก้ปัญหาพบว่าคล้ายกับปัญหาของคุณ

ทางเลือก 1

ฉันต้องการที่จะชนนี้ฉันมีปัญหากับเซิร์ฟเวอร์การผลิตใหม่กลับมาตอบสนอง 400 ที่ไม่คาดคิดเมื่อเทียบกับการพัฒนาและสภาพแวดล้อมการทดสอบการทำงานตามที่คาดไว้; เพียงติดตั้ง apt install php7.0-curl แก้ไข

มันเป็น Ubuntu 16.04 LTS ใหม่เอี่ยมที่ติดตั้งด้วย php ติดตั้งผ่าน ppa: ondrej / php ระหว่างการดีบักฉันสังเกตเห็นว่าส่วนหัวนั้นแตกต่างกัน ทั้งสองกำลังส่งแบบฟอร์มหลายส่วนที่มีข้อมูลที่ถูกจับอย่างไรก็ตามไม่มี php7.0-curl มันกำลังส่งการเชื่อมต่อ: ปิดส่วนหัวมากกว่าคาด: 100- ดำเนินการต่อ; คำขอทั้งสองซึ่งมีการเข้ารหัสการถ่ายโอน: chunked

  ทางเลือก 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());

Guzzle ต้องการ cactching หากรหัสการตอบสนองไม่ใช่ 200

ทางเลือก 3

ในกรณีของฉันมันเป็นเพราะฉันได้ผ่านอาร์เรย์ที่ว่างใน $ options ['json'] ของฉันฉันไม่สามารถทำซ้ำ 500 บนเซิร์ฟเวอร์โดยใช้ Postman หรือ cURL แม้ว่าจะผ่านส่วนหัวคำขอ Content-Type: application / json

อย่างไรก็ตามการลบคีย์ json ออกจากอาร์เรย์ตัวเลือกของคำขอแก้ไขปัญหา

ฉันใช้เวลา 30 นาทีพยายามคิดว่าผิดเพราะพฤติกรรมนี้ไม่สอดคล้องกันมาก สำหรับคำขออื่นทั้งหมดที่ฉันทำอยู่การส่งตัวเลือก $ ['json'] = [] ไม่ทำให้เกิดปัญหาใด ๆ อาจเป็นปัญหาเกี่ยวกับเซิร์ฟเวอร์ฉันไม่ได้ควบคุมเซิร์ฟเวอร์

ส่งข้อเสนอแนะเกี่ยวกับรายละเอียดที่ได้รับ


ดี ... เพื่อให้ได้คำตอบที่รวดเร็วและแม่นยำยิ่งขึ้น ฉันใช้ความคิดริเริ่มในการโพสต์คำถามในหน้าโครงการบน GitHub ฉันหวังว่าคุณจะไม่สนใจ github.com/guzzle/guzzle/issues/2599
PauloBoaventura

1
a ConnectExceptionไม่มีการตอบกลับที่เกี่ยวข้องดังนั้นจึงไม่มีข้อผิดพลาด 400 หรือ 500 เท่าที่ฉันทราบ ดูเหมือนว่าคุณควรจะจับBadResponseException(หรือClientException(4xx) / ServerException(5xx) ซึ่งเป็นลูกของมัน)
Flame


2

Guzzle ใช้ Promises สำหรับคำขอทั้งแบบซิงโครนัสและแบบอะซิงโครนัส ความแตกต่างเพียงอย่างเดียวคือว่าเมื่อคุณใช้คำขอซิงโคร (กรณีของคุณ) - มันเป็นจริงได้ทันทีโดยเรียกวิธีการwait() หมายเหตุส่วนนี้:

การเรียกwaitตามสัญญาที่ถูกปฏิเสธจะทำให้เกิดข้อยกเว้น หากเหตุผลการปฏิเสธเป็นตัวอย่างของ\Exceptionเหตุผลจะถูกโยน มิฉะนั้น a GuzzleHttp\Promise\RejectionException จะถูกโยนทิ้งและเหตุผลสามารถรับได้โดยการเรียกใช้getReason เมธอดของข้อยกเว้น

ดังนั้นมันจะพ่นRequestExceptionซึ่งเป็นตัวอย่างของ\Exceptionและมันมักจะเกิดขึ้นกับข้อผิดพลาด HTTP 4xx และ 5xx เว้นแต่ว่าการโยนข้อยกเว้นถูกปิดใช้งานผ่านตัวเลือก อย่างที่คุณเห็นมันอาจจะโยนRejectionExceptionถ้าเหตุผลไม่ใช่ตัวอย่างของ\Exceptionเช่นถ้าเหตุผลคือสตริงที่ดูเหมือนว่าเกิดขึ้นในกรณีของคุณ สิ่งประหลาดคือคุณได้รับRejectExceptionมากกว่าRequestExceptionที่ Guzzle พ่นConnectExceptionข้อผิดพลาดการหมดเวลาเชื่อมต่อ อย่างไรก็ตามคุณอาจพบเหตุผลหากคุณผ่านการRejectExceptionติดตามสแต็กใน Sentry และค้นหาว่ามีreject()การเรียกเมธอดบน Promise อย่างไร


1

พูดคุยกับผู้แต่งในส่วนความคิดเห็นเพื่อเป็นคำตอบของฉัน:

คำถาม:

คุณมีมิดเดิลแวร์ guzzle แบบกำหนดเองอยู่แล้ว (คำใบ้: HandlerStack)?

คำตอบของผู้เขียน:

มีหลากหลาย แต่มิดเดิลแวร์นั้นเป็นตัวปรับเปลี่ยนการร้องขอ / ตอบสนองแม้กระทั่ง guzzle ที่ฉันทำในนั้นก็มีการซิงโครไนซ์


ตามนี้เป็นวิทยานิพนธ์ของฉัน:

คุณมีเวลาในหนึ่งในมิดเดิลแวร์ของคุณซึ่งเรียก guzzle ดังนั้นลองใช้กรณีที่ทำซ้ำได้

ที่นี่เรามีมิดเดิลแวร์แบบกำหนดเองซึ่งเรียก guzzle และส่งคืนความล้มเหลวในการปฏิเสธด้วยข้อความยกเว้นของการเรียกใช้ย่อย มันค่อนข้างยุ่งยากเพราะการจัดการข้อผิดพลาดภายในทำให้มองไม่เห็นภายใน stack-trace

function custom_middleware(string $baseUri = 'http://127.0.0.1:8099', float $timeout = 0.2)
{
    return function (callable $handler) use ($baseUri, $timeout) {
        return function ($request, array $options) use ($handler, $baseUri, $timeout) {
            try {
                $client = new GuzzleHttp\Client(['base_uri' => $baseUri, 'timeout' => $timeout,]);
                $client->get('/a');
            } catch (Exception $exception) {
                return \GuzzleHttp\Promise\rejection_for($exception->getMessage());
            }
            return $handler($request, $options);
        };
    };
}

นี่คือตัวอย่างทดสอบวิธีที่คุณสามารถใช้:

$baseUri = 'http://127.0.0.1:8099'; // php -S 127.0.0.1:8099 test.php << includes a simple sleep(10); statement
$timeout = 0.2;

$handler = \GuzzleHttp\HandlerStack::create();
$handler->push(custom_middleware($baseUri, $timeout));

$client = new Client([
    'handler' => $handler,
    'base_uri' => $baseUri,
]);

try {
    $response = $client->get('/b');
} catch (Exception $exception) {
    var_dump(get_class($exception), $exception->getMessage());
}

ทันทีที่ฉันทำการทดสอบกับสิ่งที่ฉันได้รับ

$ php test2.php 
string(37) "GuzzleHttp\Promise\RejectionException"
string(174) "The promise was rejected with reason: cURL error 28: Operation timed out after 202 milliseconds with 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)"

ดังนั้นดูเหมือนว่าการเรียก guzzle หลักของคุณล้มเหลว แต่ในความเป็นจริงแล้วมันคือ sub-call ที่ล้มเหลว

แจ้งให้เราทราบหากสิ่งนี้ช่วยให้คุณระบุปัญหาเฉพาะของคุณ ฉันจะขอบคุณมากหากคุณสามารถแบ่งปันมิดเดิ้ลแวร์ของคุณเพื่อแก้ไขข้อบกพร่องนี้ต่อไปอีกเล็กน้อย


ดูเหมือนว่าคุณพูดถูก! ฉันถูกเรียกrejection_for($e->getMessage())แทนrejection_for($e)ที่ไหนสักแห่งในตัวกลางที่ ผมกำลังมองไปที่ต้นฉบับตัวกลางค่าเริ่มต้น (เช่นที่นี่: github.com/guzzle/guzzle/blob/master/src/Middleware.php#L106 ) แต่ค่อนข้าง couldnt บอกว่าทำไมมีแทนrejection_for($e) throw $eมันดูเหมือนว่าจะเรียงกันตามวิธีทดสอบของฉัน ดูโพสต์ต้นฉบับสำหรับ testcase ง่าย
Flame

1
@ Flame ดีใจที่ฉันสามารถช่วยคุณ :) ตามคำถามที่สองของคุณ: หากมีความแตกต่างระหว่างพวกเขา มันขึ้นอยู่กับการใช้งานจริง ๆ ในสถานการณ์เฉพาะของคุณจะไม่สร้างความแตกต่างใด ๆ (ยกเว้นคลาสยกเว้นที่ใช้) เนื่องจากคุณมีการโทรเพียงครั้งเดียว หากคุณพิจารณาที่จะเปลี่ยนเป็นการโทรหลายสายและ async ในคราวเดียวคุณควรพิจารณาใช้สัญญาเพื่อหลีกเลี่ยงการรบกวนของรหัสขณะที่คำขออื่นยังคงทำงานอยู่ ในกรณีที่คุณต้องการข้อมูลเพิ่มเติมเพื่อรับคำตอบโปรดแจ้งให้เราทราบ :)
Christoph Kluge

0

สวัสดีฉันไม่เข้าใจว่าคุณแก้ปัญหาได้หรือไม่

ดีฉันต้องการให้คุณโพสต์บันทึกข้อผิดพลาดคืออะไร ค้นหาทั้งใน PHP และภายในบันทึกข้อผิดพลาดของเซิร์ฟเวอร์

ฉันรอความคิดเห็นของคุณ


1
มีการโพสต์ข้อยกเว้นด้านบนแล้วไม่มีอะไรจะโพสต์มากไปกว่าการโพสต์มาจากกระบวนการพื้นหลังและบรรทัดที่พ่น$client->request('GET', ...)( คือไคลเอนต์ guzzle ปกติ)
Flame

0

เนื่องจากสิ่งนี้เกิดขึ้นเป็นครั้งคราวในสภาพแวดล้อมของคุณและเป็นการยากที่จะทำซ้ำการขว้างRejectionException(อย่างน้อยฉันก็ทำไม่ได้) คุณสามารถเพิ่มcatchบล็อกอื่นเข้าไปในโค้ดของคุณดูด้านล่าง:

try {
    $c = new \GuzzleHttp\Client([
        'timeout' => 0.1
    ]);
    $response = (string) $c->get('https://example.com')->getBody();
} catch (GuzzleHttp\Promise\RejectionException $e) {
    // Log the output of $e->getTraceAsString();
} 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.
}

มันจะต้องให้แนวคิดบางอย่างกับคุณและเราเกี่ยวกับสาเหตุและเมื่อเกิดเหตุการณ์เช่นนี้


น่าเศร้าที่มันไม่ได้ ฉันได้สแต็คเทรซใน Sentry เพราะไม่จับมันในที่สุดก็มาถึงตัวจัดการข้อยกเว้น Laravel (และถูกส่งไปยัง Sentry) การติดตามสแต็กจะชี้เฉพาะฉันลึกเข้าไปในห้องสมุด Guzzle แต่ฉันไม่สามารถเข้าใจได้ว่าทำไมมันถึงได้รับสัญญา
Flame

ดูคำตอบอื่นของฉันเกี่ยวกับสาเหตุที่ทำให้สัญญา: stackoverflow.com/a/60498078/1568963
Vladimir
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.