【问题标题】:How to perform concurrent requests with GuzzleHttp如何使用 GuzzleHttp 执行并发请求
【发布时间】:2018-11-13 13:07:52
【问题描述】:

如何使用 Guzzle 6 在以下条件下创建 5 个异步请求:

  • 所有请求同时开始
  • 我希望所有请求的超时值为 500 毫秒。如果请求超时,我不希望它中断其他请求
  • 如果请求返回非 200,我不希望它中断其他请求。
  • 所有请求都在不同的域上...(所以我不确定这与 base_uri 设置是否相符...

如果所有 5 个请求都返回 200OK

但是,如果说其中 2 个没有 200 并且其中 1 个超时(超过 500 毫秒),我希望仍然能够访问 2 个成功的响应。

编辑到目前为止一切正常,除了超时仍然引发异常

这是我目前所拥有的:

<?php

  require __DIR__.'/../vendor/autoload.php';

  use GuzzleHttp\Client;
  use GuzzleHttp\Promise;

  $client = new Client([
    'http_errors'     => false,
    'connect_timeout' => 1.50, //////////////// 0.50
    'timeout'         => 2.00, //////////////// 1.00
    'headers' => [
      'User-Agent' => 'Test/1.0'
    ]
  ]);

  // initiate each request but do not block
  $promises = [
    'success'            => $client->getAsync('https://httpbin.org/get'),
    'success'            => $client->getAsync('https://httpbin.org/delay/1'),
    'failconnecttimeout' => $client->getAsync('https://httpbin.org/delay/2'),
    'fail500'            => $client->getAsync('https://httpbin.org/status/500'),
  ];

  // wait on all of the requests to complete. Throws a ConnectException if any
  // of the requests fail
  $results = Promise\unwrap($promises);

  // wait for the requests to complete, even if some of them fail
  $results = Promise\settle($promises)->wait();

【问题讨论】:

  • 你看过stackoverflow answerGuzzle doc吗?如果请求成功,则返回一个 Guzzle\Http\Message\Response 对象数组。 “单个请求失败不会导致整个请求池失败。传输请求池时引发的任何异常都将汇总为Guzzle\Common\Exception\MultiTransferException 异常。“这是他们文档中的评论。
  • 是的,我只是想让它不会引发异常,其他请求可以正常继续。我将使用我拥有的最新版本更新我的代码
  • 谢谢我看那个答案,虽然我不经常使用 PHP,所以这有点让人头疼。
  • 所以目前,如果 5 个请求中的任何一个以 500 失败,则整个响应都会失败。如何让每个请求静默失败,以便我可以在成功响应时返回 200 OK?
  • 所以我可以使用 'http_errors' =&gt; false... 剩下的唯一事情是超时仍然会引发异常

标签: php guzzle guzzle6


【解决方案1】:

Guzzle 在池中提供 fulfilledrejected 回调。在这里我根据你的价值观进行了测试,在Guzzle docs阅读更多内容:

    $client = new Client([
        'http_errors'     => false,
        'connect_timeout' => 0.50, //////////////// 0.50
        'timeout'         => 1.00, //////////////// 1.00
        'headers' => [
          'User-Agent' => 'Test/1.0'
        ]
      ]);

$requests = function ($total) {
    $uris = [
        'https://httpbin.org/get',
        'https://httpbin.org/delay/1',
        'https://httpbin.org/delay/2',
        'https://httpbin.org/status/500',
        ];
    for ($i = 0; $i < count($uris); $i++) {
        yield new Request('GET', $uris[$i]);
    }
};

$pool = new Pool($client, $requests(8), [
    'concurrency' => 10,
    'fulfilled' => function ($response, $index) {
        // this is delivered each successful response
        print_r($index."fulfilled\n");
    },
    'rejected' => function ($reason, $index) {
        // this is delivered each failed request
        print_r($index."rejected\n");
    },
]);
// Initiate the transfers and create a promise
$promise = $pool->promise();
// Force the pool of requests to complete.
$promise->wait();

回复

0fulfilled
3fulfilled
1rejected
2rejected

如果您想使用上面的代码,您还可以在 $promises 中传递响应状态,这是一个示例:

use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;
....
$client = new Client([
    'http_errors'     => false,
    'connect_timeout' => 1.50, //////////////// 0.50
    'timeout'         => 2.00, //////////////// 1.00
    'headers' => [
      'User-Agent' => 'Test/1.0'
    ]
  ]);

            $promises = [
        'success' => $client->getAsync('https://httpbin.org/get')->then(
            function (ResponseInterface $res) {
                echo $res->getStatusCode() . "\n";
            },
            function (RequestException $e) {
                echo $e->getMessage() . "\n";
                echo $e->getRequest()->getMethod();
            }
        )
        ,
        'success' => $client->getAsync('https://httpbin.org/delay/1')->then(
            function (ResponseInterface $res) {
                echo $res->getStatusCode() . "\n";
            },
            function (RequestException $e) {
                echo $e->getMessage() . "\n";
                echo $e->getRequest()->getMethod();
            }
        ),
        'failconnecttimeout' => $client->getAsync('https://httpbin.org/delay/2')->then(
            function (ResponseInterface $res) {
                echo $res->getStatusCode() . "\n";
            },
            function (RequestException $e) {
                echo $e->getMessage() . "\n";
                echo $e->getRequest()->getMethod();
            }
        ),
        'fail500' => $client->getAsync('https://httpbin.org/status/500')->then(
            function (ResponseInterface $res) {
                echo $res->getStatusCode() . "\n";
            },
            function (RequestException $e) {
                echo $e->getMessage() . "\n";
                echo $e->getRequest()->getMethod();
            }
        ),
      ];

  $results = Promise\settle($promises)->wait();

【讨论】:

  • 哇,这太棒了。我会回答这个问题,但在此之前...在等待答案时,我只是这样做了...我用try {} 包裹了Promise\unwrap,然后我捕获了GuzzleHttp\Exception\ConnectException 的异常...这样做与你的方式相比有什么缺点(这要复杂得多)?
  • 如果你不想使用 Pool 并且它在文档中明确提到它会抛出 ConnectException 所以使用 try 块是绝对可以的
  • 你为什么要访问它的属性?您可以使用$response-&gt;getStatusCode()$response-&gt;getReasonPhrase() 来确定响应状态
  • 查看我更新的答案以获得另一种方式来获得您的回复,我会尽量避免以这种方式使用属性。
  • ok....你的代码也是一个大问题,你是一起使用解决和解包吗?你应该只使用一种方法,更新pastebin.com/2bczhK6R
猜你喜欢
  • 2015-05-08
  • 1970-01-01
  • 2015-03-05
  • 2016-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多