最近在对老旧的PHP服务做性能调优,使用了一个架构组基于Guzzle
写的第三方调用组件,虽然看起来写法是并发的,但是服务整体的耗时佐证了是串行的。在前年的时候自己也在公司内部写了一个基于Guzzle
的第三方调用组件,里面集成了将多个服务并行发起请求的能力,但是最近在一个业务方实际使用的时候,发现其实也是串行的。
原始的写法
最开始是从所有的添加request
的service
中遍历出所有的req
后一起放到Promise\settle
中,这种方式看似是PHP意义上的并发,具体的示例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public static function send()
{
......
$promises = [];
foreach (self::$clientList as $httpClient) {
$client_promises = $httpClient->getAllRequestPromises();
foreach ($client_promises as $item) {
$promises[] = $item;
}
}
$results = [];
$responses = Promise\settle($promises)->wait();
......
}
|
这种请求,看起来每个$promise
间是独立的,其实对于Guzzle
来说底层来说只有一个client
内的promise
才会并发处理,我在对Guzzle
底层进行了多级debug后,验证了这个逻辑。其实这儿也可以去看Guzzle的官方示例提供的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| use GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// Initiate each request but do not block
$promises = [
'image' => $client->getAsync('/image'),
'png' => $client->getAsync('/image/png'),
'jpeg' => $client->getAsync('/image/jpeg'),
'webp' => $client->getAsync('/image/webp')
];
// Wait for the requests to complete; throws a ConnectException
// if any of the requests fail
$responses = Promise\Utils::unwrap($promises);
// You can access each response using the key of the promise
echo $responses['image']->getHeader('Content-Length')[0];
echo $responses['png']->getHeader('Content-Length')[0];
// Wait for the requests to complete, even if some of them fail
$responses = Promise\Utils::settle($promises)->wait();
// Values returned above are wrapped in an array with 2 keys: "state" (either fulfilled or rejected) and "value" (contains the response)
echo $responses['image']['state']; // returns "fulfilled"
echo $responses['image']['value']->getHeader('Content-Length')[0];
echo $responses['png']['value']->getHeader('Content-Length')[0];
|
新的写法
在确认了只有这种写法才能并发后,就需要对原先的写法做改造了,整体的思路和官方处理保持一致,在真正send的时候重新实例化一个client
,将获取到的promise
内的请求参数重新放到client
中,具体的改造如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public static function send()
{
......
$client = new Client();
$promises = [];
foreach (self::$clientList as $httpClient) {
$client_promises = $httpClient->getAllRequestPromises();
foreach ($client_promises as $item) {
$promises[] = $client->requestAsync($item['method'], rtrim($item['bese_uri'], '/') . '/' . ltrim($item['uri'], '/'), $item['options']);
}
}
$results = [];
$responses = Promise\settle($promises)->wait();
......
}
|
效果
接口的P95下降降300ms,avg下降80ms,大家在使用的时候还是要多看官方文档,多实践,毕竟实践是检验真理的唯一标准