发送多个请求时如何将结果与请求匹配?

时间:2014-03-26 01:38:15

标签: guzzle

:一种。摘要

作为标题,Guzzle允许一次发送多个请求以节省时间,如documentation

$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

创建之后,Guzzle回复2位置回来:

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

使用上述方法链接对其请求的响应,我们仍然不知道哪个响应是针对哪篇文章的,因为请求对象完全相同

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

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")

这已经困扰了我好几个月,仍然无法解决这个问题。

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

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

感谢您阅读这篇长篇文章。

6 个答案:

答案 0 :(得分:1)

我遇到了同样的问题。

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

$responses = $client->send($requests)之后,您可以遍历响应并检索有效网址$response->getEffectiveUrl()并对其进行解析(请参阅parse_urlparse_str)以获取自定义参数(使用唯一ID)并搜索您拥有它的请求数组。

答案 1 :(得分:1)

我找到了正确的方法,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对每个相应的请求都有结果。

e.g。 $ allResults [&#39; key_1&#39;]是$ allRequests [&#39; key_1&#39;]

的结果

答案 2 :(得分: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();

答案 3 :(得分:0)

我是这样做的:

id <- factor("384508338244525230_47603942")
(id_ <- as.numeric(sub("([0-9]+)_.*", "\\1", id)))
# [1] 3.845083e+17
format(id_, scientific = FALSE)
# [1] "384508338244525230"

答案 4 :(得分:0)

与新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) );

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

答案 5 :(得分: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
}