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

8次阅读

共计 1440 个字符,预计需要花费 4 分钟才能阅读完成。

前言

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

正文完
 0