Swoole协程并发调用实践—解决串行IO阻塞问题

php基础

浏览数:121

2019-8-6

前言

在传统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等待问题,带来的提升是巨大的。希望本篇文章对你有帮助!

作者:MeetMax