耗时异步请求不是真的异步吗? [英] Guzzle async requests not really async?

查看:82
本文介绍了耗时异步请求不是真的异步吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在尝试使用guzzle进行并发异步请求.经过一些资源后,例如

We are trying to do concurrent asynchronous requests using guzzle. After going through a few resources, like this and this, we came up with some code that is shared below. However it is not working as expected.

似乎Guzzle正在同步执行这些请求,而不是异步执行.

It looks like Guzzle is doing these request synchronously rather than async.

仅出于测试目的,我们点击了一个内部网址,该网址进行了5秒钟的睡眠.并发性为10时,我们希望所有10个请求最初都将排队并同时发送到服务器几乎,在那里它们将等待5秒钟,然后所有几乎这些将几乎同时完成.如此一来,耗时的客户端就会收到来自迭代器的10个新请求,依此类推.

Just for test purposes, we are hitting an internal url, which does a 5 second sleep. With a concurrency of 10 we expect that all 10 requests will initially be queued and send to the server almost simultaneously, where they will wait for 5 seconds, and will then almost all of those will finish nearly at the same time. Which would make the guzzle client to pick up 10 new requests from iterator and so on.

    $iterator = function() {
        $index = 0;
        while (true) {
            $client = new Client(['timeout'=>20]);
            $url = 'http://localhost/wait/5' . $index++;
            $request = new Request('GET',$url, []);
            echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;
            yield $client
                ->sendAsync($request)
                ->then(function(Response $response) use ($request) {
                    return [$request, $response];
                });
        }
    };

    $promise = \GuzzleHttp\Promise\each_limit(
        $iterator(),
        10,  /// concurrency,
        function($result, $index) {
            /** GuzzleHttp\Psr7\Request $request */
            list($request, $response) = $result;
            echo (string) $request->getUri() . ' completed '.PHP_EOL;
        },
        function(RequestException $reason, $index) {
            // left empty for brevity
        }
    );
    $promise->wait();

实际结果

我们发现Guzzle在第一个请求完成之前从未提出第二个请求.等等.

Actual Results

We find that that Guzzle never made a second request until the first one is finished. and so on.

Queuing http://localhost/wait/5/1 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/2 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/3 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/4 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/5 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/6 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/7 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/8 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/9 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/10 @ 2017-09-01 17:15:28
http://localhost/wait/5/1 completed
Queuing http://localhost/wait/5/11 @ 2017-09-01 17:15:34
http://localhost/wait/5/2 completed
Queuing http://localhost/wait/5/12 @ 2017-09-01 17:15:39
http://localhost/wait/5/3 completed
Queuing http://localhost/wait/5/13 @ 2017-09-01 17:15:45
http://localhost/wait/5/4 completed
Queuing http://localhost/wait/5/14 @ 2017-09-01 17:15:50 

操作系统/版本信息

  • Ubuntu
  • PHP/7.1.3
  • GuzzleHttp/6.2.1
  • curl/7.47.0

问题可能出在\ GuzzleHttp \ Promise \ each_limit ..可能无法快速启动或解决承诺.我们可能有可能不得不把它诱骗到外部tick.

The issue could be with \GuzzleHttp\Promise\each_limit .. which perhaps does not initiates or resolves the promise fast enough. It may be possible that we have to trick that into ticking externally.

推荐答案

在示例代码中,您正在为要发出的每个请求创建一个新的GuzzleHttp\Client实例.这似乎并不重要,但是,在实例化GuzzleHttp\Client期间,它将设置默认的处理程序(如果未提供). (然后,该值将传递给通过客户端发送的任何请求,除非被覆盖.)

In the example code, you're creating a new GuzzleHttp\Client instance for every request you want to make. This might not seem important, however, during instantiation of GuzzleHttp\Client it will set a default handler if none is provided. (This value is then passed down to any request being sent through the Client, unless it is overridden.)

注意:它从此<确定<函数.不过,很可能最终会默认为curl_mutli_exec.

Note: It determines the best handler to use from this function. Though, it'll most likely end up defaulting to curl_mutli_exec.

这有什么重要性?底层处理程序负责同时跟踪和执行多个请求.通过每次创建一个新的处理程序,您的所有请求都不会被正确地分组并一起运行.有关此内容的更多信息,请细读 curl_multi_exec文档.

What's the importance of this? It's the underlying handler that is responsible for tracking and executing multiple requests at the same time. By creating a new handler every time, none of your requests are properly being grouped up and ran together. For some more insight into this take a gander into the curl_multi_exec docs.

因此,您有两种处理方式:

So, you kind of have two ways of dealing with this:

通过客户端到达迭代器:

Pass through the client through to the iterator:

$client = new GuzzleHttp\Client(['timeout' => 20]);

$iterator = function () use ($client) {
    $index = 0;
    while (true) {
        if ($index === 10) {
            break;
        }

        $url = 'http://localhost/wait/5/' . $index++;
        $request = new Request('GET', $url, []);

        echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;

        yield $client
            ->sendAsync($request)
            ->then(function (Response $response) use ($request) {
                return [$request, $response];
            });

    }
};

$promise = \GuzzleHttp\Promise\each_limit(
    $iterator(),
    10,  /// concurrency,
    function ($result, $index) {
        /** @var GuzzleHttp\Psr7\Request $request */
        list($request, $response) = $result;
        echo (string)$request->getUri() . ' completed ' . PHP_EOL;
    }
);
$promise->wait();

或在其他位置创建处理程序并将其传递给客户端:(尽管我不确定为什么要这样做,但是它在那里!)

or create the handler elsewhere and pass it to the client: (Though I'm not sure why you'd do this, but it's there!)

$handler = \GuzzleHttp\HandlerStack::create();

$iterator = function () use ($handler) {
    $index = 0;
    while (true) {
        if ($index === 10) {
            break;
        }

        $client = new Client(['timeout' => 20, 'handler' => $handler])
        $url = 'http://localhost/wait/5/' . $index++;
        $request = new Request('GET', $url, []);

        echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;

        yield $client
            ->sendAsync($request)
            ->then(function (Response $response) use ($request) {
                return [$request, $response];
            });

    }
};

$promise = \GuzzleHttp\Promise\each_limit(
    $iterator(),
    10,  /// concurrency,
    function ($result, $index) {
        /** @var GuzzleHttp\Psr7\Request $request */
        list($request, $response) = $result;
        echo (string)$request->getUri() . ' completed ' . PHP_EOL;
    }
);
$promise->wait();

这篇关于耗时异步请求不是真的异步吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆