GuzzleHttp 并发请求应该怎么写?

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => API_URL]);
$promises = [];
$promises[] = $client->getAsync('/path');
$promises[] = $client->getAsync('/path');
$promises[] = $client->getAsync('/path');
$promises[] = $client->getAsync('/path');
$responses = Promise\Utils::unwrap($promises);
foreach ($responses as $key => $response) {
    // todo 
}

想请问一下,我使用 GuzzleHttp 这样写的请求是顺序执行的,不是并发请求。
应该怎么写才是并发请求?

参考文档
docs.guzzlephp.org/en/stable/quick...

感谢阅读,有收获的话不妨点个赞:smiling_imp:
讨论数量: 17
CodingHePing

我记得有个请求池

1年前 评论
CodingHePing
1年前 评论
declandragon (楼主) 1年前
use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;

$client = new Client();

$requests = function ($total) {
    $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
    for ($i = 0; $i < $total; $i++) {
        yield new Request('GET', $uri);
    }
};

$pool = new Pool($client, $requests(100), [
    'concurrency' => 5,
    'fulfilled' => function ($response, $index) {
        // this is delivered each successful response
    },
    'rejected' => function ($reason, $index) {
        // this is delivered each failed request
    },
]);

// Initiate the transfers and create a promise
$promise = $pool->promise();

// Force the pool of requests to complete.
$promise->wait();
1年前 评论
declandragon (楼主) 1年前

楼主你写的是异步

1年前 评论
declandragon (楼主) 1年前
AloneUtopia

会不会这里所说的”并发”,其实就只是按顺序发起异步请求(这一部分执行很快很快,看起来就像是”并发”了),然后等所有请求完成响应… :sweat_smile:

1年前 评论
$requests = function ($total, $client) {
    $uri = 'https://baidu.com/';
    for ($i = 0; $i < $total; $i++) {
        yield function () use ($uri, $client) {
            return $client->requestAsync('GET', $uri, []);
        };
    }
};

$pool = new Pool($client, $requests(10, $client), [
    'concurrency' => 10,
    'fulfilled'   => function (Response $response, $index) {
        // this is delivered each successful response
        print_r(json_decode($response->getBody()->getContents(), true));
        echo '啊' . $index. '时间:'.microtime() . PHP_EOL;
    },
    'rejected'    => function (RequestException $reason, $index) {
        // this is delivered each failed request
        echo $reason->getMessage();
        echo '额' . $index. '时间:'.microtime() . PHP_EOL;
    },
]);

// Initiate the transfers and create a promise
$promise = $pool->promise();
$promise->wait();

执行的结果

PHP

所谓并发其实还有有个先后顺序,只不过差距很小,可以hu

如果改成下面这样执行

$requests = function ($total, $client) {
    $uri = 'https://baidu.com/';
    for ($i = 0; $i < $total; $i++) {
        yield function () use ($uri, $client) {
            return $client->request('GET', $uri, []);
        };
    }
};

执行结果

PHP

异步并发:做个比喻,就好比田径比赛,8个人一起从起点开始(并发),相当于8个线程,发令枪一响,同时开始,到达终点有先后顺序(异步),并不是按照赛道顺序。如果理解有误,欢迎指正

1年前 评论

@Geroge 感谢回复,我的理解是跟你一样的,到达时间有先后,但是随着比赛的人增加,最后到达的那个人,不应该越来越慢。

我这边的例子就是请求数量越多,总耗时越长,所以我的结论是顺序请求【类似于接力比赛】,一个请求结束了,才请求下一个。

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$t1 = microtime(true);
$client = new Client(['base_uri' => 'https://baidu.com']);
$promises = [];
$total = 60;
for ($i = 0; $i < $total; $i++) {
    $promises[] = $client->getAsync('/');
}
$responses = Promise\Utils::unwrap($promises);
$t2 = microtime(true);

echo '请求次数 ' . $total . ' 耗时:' . ($t2 - $t1) . PHP_EOL;

file

你的代码改成这个样子,是不是也能得出和我一样的结论?

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Response;

$t1 = microtime(true);
$client = new Client();
$requests = function ($total, $client) {
    $uri = 'https://baidu.com/';
    for ($i = 0; $i < $total; $i++) {
        yield function () use ($uri, $client) {
            return $client->requestAsync('GET', $uri, []);
        };
    }
};
$total =  70;
$pool = new Pool($client, $requests($total, $client), [
    'concurrency' => $total,
    'fulfilled' => function (Response $response, $index) {},
    'rejected' => function (RequestException $reason, $index) {},
]);
$promise = $pool->promise();
$promise->wait();
$t2 = microtime(true);

echo '请求次数 ' . $total . ' 耗时:' . ($t2 - $t1) . PHP_EOL;

PHP

1年前 评论
declandragon (作者) (楼主) 1年前

以下代码并发请求10次。请求响应内容为:{请求发起序号}:{请求响应序号}
根据实际测试,响应序号与发起序号无强相关,所以证明是并发的,而且/test/index中有1秒睡眠,10次请求实际只用4.08s,也证明了请求是并发的


    // /test/index
    public function indexAction(){
        sleep(1);
        echo ($_GET['i']??0).':'.\App\Common\Cache::redis()->incr('test');
        return false;
    }

    // /test/g
    public function gAction(){
        $client = new \GuzzleHttp\Client(['base_uri' => 'http://'.$_SERVER['SERVER_NAME'].'/']);


        $promises=[];
        for($i=0;$i<10;$i++){
            $promises[]=$client->getAsync('/test/index?i='.$i);
        }
        $responses = \GuzzleHttp\Promise\Utils::unwrap($promises);
        foreach ($responses as $response){
            echo $response->getBody()->getContents(),"\n";
        }
        return false;
    }

response:4.08s

0:2
1:1
2:3
3:5
4:4
5:6
6:7
7:8
8:9
9:10

结论是:$responses数组排序与$promises数组的排序一致,与实际请求和响应无关,GuzzleHttp是可以并发的

1年前 评论
PHPer技术栈 1年前
renxiaotu (作者) 1年前

单线程只能顺序执行哦,要实现并发得多线程

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!