5

总结

正如其标题,Guzzle 允许一次发送多个请求以节省时间,如文档中所示。

$responses = $client->send(array(
    $requestObj1,
    $requestObj2,
    ...
));

(given that each request object is an instance of
Guzzle\Http\Message\EntityEnclosingRequestInterface)

当响应返回时,为了识别哪个响应是针对哪个请求的,我们可以遍历每个请求并获取响应(仅在执行上述命令后可用):

$response1 = $requestObj1->getResponse();
$response2 = $requestObj2->getResponse();
...

B. 问题

如果请求对象包含相同的数据。无法识别原始请求。

假设我们有以下场景,我们需要创建 2 篇文章:远程服务器上的 A 和 B:something.com/articles/create.json

每个请求都有相同的 POST 数据:

subject: This is a test article

创建后,带有 2 个位置的 Guzzle 响应返回:

something.com/articles/223.json
something.com/articles/245.json

使用上面的方法将response-to-its-request链接起来,我们仍然不知道哪个response是针对哪篇文章的,因为request对象是完全一样的

因此,在我的数据库中,我无法写下结果:

article A -> Location: 245.json
article B -> Location: 223.json

因为它可以是相反的方式:

article A -> Location: 223.json
article B -> Location: 245.json

一种解决方案是在 POST 请求中添加一些额外的参数,例如

subject: This is a test article
record: A

但是,远程服务器将返回错误并且不会创建文章,因为它不了解关键“记录”。远程服务器是第三方服务器,我无法更改它的工作方式。

另一个适当的解决方案是在请求对象上设置一些特定的 id/tag,以便我们之后可以识别它。但是,我查看了文档,但没有方法来唯一标识请求,例如

$request->setID("id1")

or

$request->setTag("id1")

这一直困扰着我几个月,仍然无法解决这个问题。

如果您有解决方案,请告诉我。

非常感谢,你救了我!!!!

感谢您阅读这篇长文。

4

6 回答 6

2

我找到了一种正确的方法,Guzzle 允许在请求完成后添加回调。所以我们可以通过在批处理中的每个请求上设置它来实现这一点

默认情况下,每个请求都可以这样创建

$request = $client->createRequest('GET', 'http://httpbin.org', [
    'headers' => ['X-Foo' => 'Bar']
]);

所以,要实现我们想要的:

$allRequests = [];
$allResults = [];

for($k=0; $k<=10; $k++){
    $allRequests['key_'.$k] = $client->createRequest('GET', 'http://httpbin.org?id='.$k, [
        'headers' => ['X-Foo' => 'Bar'],
        'events' => [
            'complete' => function ($e) use (&$allResults, $k){
                $response = $e->getResponse();
                $allResults['key_'.$k] = $response->getBody().'';
            }
        ]
    ]);
}

$client->sendAll(array_values($allRequests));

print_r($allResults);

所以现在 $allResults 有每个相应请求的结果。

例如 $allResults['key_1'] 是 $allRequests['key_1'] 的结果

于 2014-10-12T10:43:45.627 回答
1

我遇到了同样的问题。

我通过添加具有为每个请求生成的唯一 id 的自定义查询参数并将其添加到请求 url 来解决它(您需要为每个请求记住这个 id 以便在之后解决它)。

$responses = $client->send($requests)您可以遍历响应并检索有效 url$response->getEffectiveUrl()并对其进行解析(请参阅parse_urlparse_str)以获取自定义参数(具有唯一 id)并在您的请求数组中搜索哪个拥有它之后。

于 2014-09-25T04:20:22.410 回答
1

我找到了一个更好的答案。

我一次发送 20 个请求的批次,同时发送 4 个请求,并使用了池化技术,我得到了满足,然后又被拒绝了,如文档中所述。

我发现在生成/构建数组时,我可以将此代码添加到我的 requestAsync() 函数调用的末尾(我在不同的地方都这样做)。

$request = $request->then(function (\GuzzleHttp\Psr7\Response $response) use ($source_db_object) {
            $response->_source_object = $source_db_object;
            return $response;
});

然后在池中的 clousures 中,我可以正常访问响应上的 _source_object,效果很好。我觉得它有点 hacky,但如果你只是确定使用一个从不与 Guzzle 中的任何东西冲突的名称,这应该没问题。

这是一个完整的例子:

use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Response as GuzzleResponse;

$client = new Client();
$requests = [];

// Simple set-up here, generate some random async requests
for ($i = 0; $i < 10; $i++) {
    $request = $client->requestAsync('GET', 'https://jsonplaceholder.typicode.com/todos/1');
    // Here we can attach any identifiable data
    $request->_source_object = $i;
    array_push($requests, $request);
}

$generator = function () use($requests) {
    while ($request = array_pop($requests)) {
        yield function() use ($request) {
            return $request->then(function (GuzzleResponse $response) use ($request) {
                // Attach _source_object from request to the response
                $response->_source_object = $request->_source_object ?? [];

                return $response;
            });
        };
    }
};

$requestPool = new Pool($client, $generator(), [
    'concurrency' => 5,
    'fulfilled' => function ($response) {
        // Then we can properly access the _source_object data once response has arrived here!
        echo $response->_source_object . "\n";
    }
]);

$requestPool->promise()->wait();
于 2016-06-29T20:31:33.507 回答
0

我这样做:

// create your requests
$requests[] = $client->createRequest('GET', '/endpoint', ['config' => ['order_id' => 123]]);
...
// in your success callback get 
$id = $event->getRequest()->getConfig()['order_id']
于 2015-05-13T10:10:01.293 回答
0

与新 GuzzleHttp 相关的更新 guzzlehttp/guzzle

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

传递一组 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) );

在此示例中,您将能够使用 $requestArr[$doc->reference] 引用每个响应。简而言之,为您的数组提供一个索引并运行 Promise::unwrap 调用。

于 2015-09-23T23:21:56.133 回答
0

我也遇到过这个问题。这是出现的第一个线程。我知道这是一个已解决的线程,但我最终想出了一个更好的解决方案。这适用于所有可能遇到此问题的人。

一种选择是使用 Guzzle 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
}
于 2019-08-01T05:54:05.940 回答