【问题标题】:How to perform multiple Guzzle requests at the same time?如何同时执行多个 Guzzle 请求?
【发布时间】:2013-10-22 14:06:55
【问题描述】:

我可以使用 Guzzle 执行单个请求,我对 Guzzle 迄今为止的性能非常满意,但是,我在 Guzzle API 中读到了一些关于 MultiCurl 和 Batching 的内容。

有人可以向我解释如何同时提出多个请求吗?如果可能,异步。我不知道这是否就是 MultiCurl 的意思。同步也不是问题。我只想同时或非常接近(时间短)做多个请求。

【问题讨论】:

  • 文档中有一个demo of this。从您的角度来看,这仍然是一个同步调用,但在内部是并行的 - 因此调用的总时间将只是单个最长提取的时间。

标签: php curl guzzle


【解决方案1】:

与新 GuzzleHttp 相关的更新guzzlehttp/guzzle

并发/并行调用现在通过几种不同的方法运行,包括 Promises..Concurrent Requests

传递 RequestInterfaces 数组的旧方法将不再适用。

查看示例这里

    $newClient = new  \GuzzleHttp\Client(['base_uri' => $base]);
    foreach($documents->documents as $doc){

        $params = [
            'language' =>'eng',
            'text' => $doc->summary,
            'apikey' => $key
        ];

        $requestArr[$doc->reference] = $newClient->getAsync( '/1/api/sync/analyze/v1?' . http_build_query( $params) );
    }

    $time_start = microtime(true);
    $responses = \GuzzleHttp\Promise\unwrap($requestArr); //$newClient->send( $requestArr );
    $time_end = microtime(true);
    $this->get('logger')->error(' NewsPerf Dev: took ' . ($time_end - $time_start) );

更新: 正如 cmets 中的建议和 @sankalp-tambe 所要求的那样,您还可以使用不同的方法来避免一组失败的并发请求不会返回所有响应。

虽然 Pool 建议的选项是可行的,但我仍然更喜欢 Promise。

promise 的一个例子是使用结算和等待方法而不是 unwrap。

与上面示例的区别是

$responses = \GuzzleHttp\Promise\settle($requestArr)->wait(); 

我在下面创建了一个完整的示例,以供参考如何处理 $responses。

require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Promise as GuzzlePromise;

$client = new GuzzleClient(['timeout' => 12.0]); // see how i set a timeout
$requestPromises = [];
$sitesArray = SiteEntity->getAll(); // returns an array with objects that contain a domain

foreach ($sitesArray as $site) {
    $requestPromises[$site->getDomain()] = $client->getAsync('http://' . $site->getDomain());
}

$results = GuzzlePromise\settle($requestPromises)->wait();

foreach ($results as $domain => $result) {
    $site = $sitesArray[$domain];
    $this->logger->info('Crawler FetchHomePages: domain check ' . $domain);

    if ($result['state'] === 'fulfilled') {
        $response = $result['value'];
        if ($response->getStatusCode() == 200) {
            $site->setHtml($response->getBody());
        } else {
            $site->setHtml($response->getStatusCode());
        }
    } else if ($result['state'] === 'rejected') { 
        // notice that if call fails guzzle returns is as state rejected with a reason.

        $site->setHtml('ERR: ' . $result['reason']);
    } else {
        $site->setHtml('ERR: unknown exception ');
        $this->logger->err('Crawler FetchHomePages: unknown fetch fail domain: ' . $domain);
    }

    $this->entityManager->persist($site); // this is a call to Doctrines entity manager
}

此示例代码最初发布于here

【讨论】:

  • 虽然这非常适合我一次加载多个图像 URL 数据的用例,但如何处理其中一个正在加载的 URL 引发 404 错误的情况?当这种情况发生时,Guzzle 会吓坏并抛出一个 guzzle 异常。我不能保证 URL 的可用性,所以我希望只加载多个请求并使用实际通过的请求。
  • 没关系,我只是重新编写了代码以使用 GuzzleHttp\Pool。似乎也很好用,给了我更多的控制权。
  • 你能把你的代码分享给 Pool 吗?
  • @georaldc 我不太喜欢 Pool 方法。 Sankalp,您可以在我的答案更新中看到一个完整的示例替代方案,它将返回所有响应而不是异常。
  • @Bizmate 感谢它对我有用。我很好奇并发水平以及它比旧的异步方式性能高多少?
【解决方案2】:

来自文档: http://guzzle3.readthedocs.org/http-client/client.html#sending-requests-in-parallel

有关返回映射到响应或错误的请求对象哈希的易于使用的解决方案,请参阅http://guzzle3.readthedocs.org/batching/batching.html#batching

简短示例:

<?php

$client->send(array(
    $client->get('http://www.example.com/foo'),
    $client->get('http://www.example.com/baz'),
    $client->get('http://www.example.com/bar')
));

【讨论】:

  • 我们可以将不同的数据发送到 guzzel 中的同一个 url 吗?
【解决方案3】:

Guzzle 6.0 让发送多个异步请求变得非常容易。

有多种方法可以做到这一点。

您可以创建异步请求并将生成的 Promise 添加到单个数组中,然后使用 settle() 方法获取结果,如下所示:

$promise1 = $client->getAsync('http://www.example.com/foo1');
$promise2 = $client->getAsync('http://www.example.com/foo2');
$promises = [$promise1, $promise2];

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

您现在可以遍历这些结果并使用GuzzleHttpPromiseallGuzzleHttpPromiseeach 获取响应。详情请参阅this article

如果您要发送的请求数量不确定(这里说 5 个),您可以使用GuzzleHttp/Pool::batch()。 这是一个例子:

$client = new Client();

// Create the requests
$requests = function ($total) use($client) {
    for ($i = 1; $i <= $total; $i++) {
        yield new Request('GET', 'http://www.example.com/foo' . $i);
    }
};

// Use the Pool::batch()
$pool_batch = Pool::batch($client, $requests(5));
foreach ($pool_batch as $pool => $res) {

    if ($res instanceof RequestException) {
        // Do sth
        continue;
    }

    // Do sth
}

【讨论】:

    猜你喜欢
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 2015-11-25
    • 1970-01-01
    • 2021-03-03
    相关资源
    最近更新 更多