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

银蝶
• 阅读 5213

前言

在传统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密集型场景下,异步模式带来的巨大性能提升。

串行调用、并发调用 图解

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

总结

如果大家和我一样,在IO密集型场景下,程序的速度到了瓶颈,无论怎么优化,速度都没有质的提升,那么可以尝试使用Swoole的协程模式,可能会带来意想不到的效果。在大多数Web场景下,并不是我们的程序执行慢,而是大多数时间都在等待IO结束,无论怎么优化代码提升都不明显,不如换个思路,使用协程异步来解决IO等待问题,带来的提升是巨大的。希望本篇文章对你有帮助!

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
风斗 风斗
4年前
为什么很多公司都转型go语言开发?Go语言能做什么 ?
Go语言能做什么一、我们为什么选择Go语言选择Go语言的原因可能会有很多,关于Go语言的特性、优势等,我们在之前的文档中也已经介绍了很多了。但是最主要的原因,应该是基于以下两方面的考虑:执行性能缩短API的响应时长,解决批量请求访问超时的问题。在Uwork的业务场景下,一次API批量请求,往往会涉及对另外接口服务的多次调用,而在之前的PHP实现模式下,要做到
科工人 科工人
4年前
为什么很多公司都转型go语言开发?Go语言能做什么
Go语言能做什么一、我们为什么选择Go语言选择Go语言的原因可能会有很多,关于Go语言的特性、优势等,我们在之前的文档中也已经介绍了很多了。但是最主要的原因,应该是基于以下两方面的考虑:执行性能缩短API的响应时长,解决批量请求访问超时的问题。在Uwork的业务场景下,一次API批量请求,往往会涉及对另外接口服务的多次调用,而在之前的PHP实现模式下,要做到
Karen110 Karen110
4年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
4年前
Linux下搭建PHP开发环境,Php
目前PHP项目开发几种比较流行的架构搭建中,LNMP在性能方面是最好的,正因为如此,使得LNMP架构逐渐流行起来,今天,前面提到了Nginx部署,由于项目实际环境的需要,今天就在说一下怎么部署PHP。环境CentOS6.3PHP5.6安装步骤1\.下载并安装PHP:先下载文件到对应的目录,然后解压文件。root@i
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
PHP的超时实现
我们知道PHP可以设置一个脚本执行的超时时间,也就是脚本的执行时间不能超过设置的超时时间.那么PHP是怎么做到这个功能的呢?  其实PHP的超时功能利用了操作系统的定时信号来实现的,在一个请求开始时(php\_request\_startup),PHP便会利用zend\_set\_timeout()接口来设置一个定时器信号,
Stella981 Stella981
4年前
Redis PHP连接操作
安装要在PHP程序中使用Redis,首先需要确保Redis的PHP驱动程序和PHP安装设置在机器上。可以查看PHP教程教你如何在机器上安装PHP。现在,让我们来看看一下如何设置Redis的PHP驱动程序。需要从github上资料库:https://github.com/nicolasff/phpredis(https:
Wesley13 Wesley13
4年前
PHP调用外部程序的方法
很多情况下需要php调用其他程序如shell命令、shell脚本、可执行程序等等,此时需要使用到诸如exec/system/popen/proc\_open等函数,每种函数有各自适合使用的场景以及需要注意的地方。前提:PHP没有运行在安全模式如果PHP运行在安全模式下,那么在执行外部命令、打开文件、连接数据库、基于HTTP的认证这4
Easter79 Easter79
4年前
Swoole2.0内置协程并发测试
Swoole2.0是一个革命性的版本,它内置了协程的支持。与Go语言协程不同,Swoole协程完全不需要开发者添加任何额外的关键词,直接以过去最传统的同步阻塞模式编写代码,底层自动进行协程调度实现异步IO。使并发编程变得非常简单。最新的版本中,内置协程已支持PHP7,同时兼具了性能和并发能力,Swoole的强大超乎想象。本文基于Github最新的Sw
Wesley13 Wesley13
4年前
NIO
一、什么是阻塞和非阻塞?传统的IO流都是阻塞式的。也就是说,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端