前言
在传统 PHP 的 LNMP 架构下,有些问题始终困扰着我们,由于 PHP 程序只能串行执行的特性,在 IO 密集型应用下,PHP 程序只能在 IO 操作完成后才能执行后续的代码,其中有大部分时间都在等待 IO,严重影响执行效率,这是非常不合理。现在有这样一个场景,一个接口需要调用 10 个第三方的 Http 接口才能拿到所有数据,假设每个接口调用平均耗时 300ms,在传统 PHP 的串行模式下,需要 3 秒才能执行完。使用基于 Swoole 的协程 Http 客户端可以解决这个问题,实现 Http 请求的并发调用。
实践
下面我们使用 传统 Http 客户端
和Swoole 协程客户端
做对比,对比方式为,通过连续 N 次请求淘宝首页,对比请求总响应时间,就能够直观看到并发调用的优势。
传统 Http 客户端示例
$start = microtime(true);
$n = 50;
for ($i = 0; $i < $n; $i++) {$http = new Http();
$res = $http->get('https://www.taobao.com/');
$res->getBody()->getContents();
}
$end = microtime(true);
echo bcsub($end,$start,2).PHP_EOL;
Swoole 协程 Http 客户端示例
go(function (){$start = microtime(true);
// 并发请求 n
$result = [];
$clients = [];
$n = 50;
for ($i = 0; $i < $n; $i++) {$cli = new \Swoole\Coroutine\Http\Client('www.taobao.com', 443,true);
$cli->setHeaders([
'Host' => "www.taobao.com",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$cli->set(['timeout' => 2]);
$cli->setDefer();
$cli->get('/');
$clients[] = $cli;}
for ($i = 0; $i < $n; $i++) {if (!$clients[$i]->recv()) {continue;}
$result[] = $clients[$i]->body;
}
$end = microtime(true);
echo bcsub($end,$start,2).PHP_EOL;
});
数据分析
调用次数 | 响应时间(协程) | 响应时间(传统) |
---|---|---|
10 | 1.09s | 3.64s |
20 | 2.33s | 7.27s |
30 | 2.89s | 14.91s |
50 | 3.96s | 17.57s |
100 | 7.33s | 37.23s |
通过上面的数据可以看到,协程模式速度是传统模式的好几倍,而且随着调用次数增多,协程模式的速度优势越来越明显,这就 IO 密集型场景下,异步模式带来的巨大性能提升。
串行调用、并发调用 图解
总结
如果大家和我一样,在 IO 密集型场景下,程序的速度到了瓶颈,无论怎么优化,速度都没有质的提升,那么可以尝试使用 Swoole 的协程模式,可能会带来意想不到的效果。在大多数 Web 场景下,并不是我们的程序执行慢,而是大多数时间都在等待 IO 结束,无论怎么优化代码提升都不明显,不如换个思路,使用协程异步来解决 IO 等待问题,带来的提升是巨大的。希望本篇文章对你有帮助!