协程+通道 全新的php并发编程体验

swoole从2.0版本开始内置协程,那时候协程的使用范围还很窄,很多函数和方法还不支持协程调用,协程使用还有诸多问题和不便

上个月swoole4.0版本正式发布,新版本4.0基于boost.context 1.60汇编代码实现了全新的协程内核。在保存PHP函数调用栈的基础上,增加了C栈的上下文存储。实现了对所有PHP语法的支持。现在在任意PHP的函数,包括call_user_func、反射、魔术方法、array_map中均可使用协程。

  • 4.0与2.0是100%兼容的,仅重构了协程内核,API层无变更

4.0分支代码升级至C++11标准,建议使用gcc-4.8或更高版本 支持php7.1及以上版本

在php7.1+ swoole4.0版本中,我们可以很方便的使用协程来实现并发编程

swoole支持两种方式的并发调用

方式1:并发发包

go(function(){
    //并发请求 n
    $n = 5;
    for ($i = 0; $i < $n; $i++) {
        $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
        $cli->setHeaders([
            'Host' => "local.ad.oa.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('/test.php');
        $clients[] = $cli;
    }

    for ($i = 0; $i < $n; $i++) {
        $r = $clients [$i]->recv();
        $result[] = $clients[$i]->body;
    }
})

以上流程网络IO的时间就接近于单次http网络请求的时间。

现在支持并发请求的Client有:

  1. Swoole\Coroutine\Client
  2. Swoole\Coroutine\Redis
  3. Swoole\Coroutine\MySQL
  4. Swoole\Coroutine\Http\Client

除了Swoole\Coroutine\Client,其他Client都实现了defer特性,用于声明延迟收包。 因为Swoole\Coroutine\Client的发包和收包方法是分开的,所以就不需要实现defer特性了,而其他Client的发包和收包都是在一个方法中,所以需要一个setDefer()方法声明延迟收包,然后通过recv()方法收包。

方式2:协程+通道

除了只用底层内置的setDefer机制实现并发请求之外,还可以使用子协程+通道实现并发。

go(function(){
    $chan = new chan(2);
    go(function () use ($chan) {
        $cli = new Swoole\Coroutine\Http\Client('www.qq.com', 80);
            $cli->set(['timeout' => 10]);
            $cli->setHeaders([
            'Host' => "www.qq.com",
            "User-Agent" => 'Chrome/49.0.2587.3',
            'Accept' => 'text/html,application/xhtml+xml,application/xml',
            'Accept-Encoding' => 'gzip',
        ]);
        $ret = $cli->get('/');
        $chan->push(['www.qq.com' => $cli->body]);
    });

    go(function () use ($chan) {
        $cli = new Swoole\Coroutine\Http\Client('www.163.com', 80);
        $cli->set(['timeout' => 10]);
        $cli->setHeaders([
            'Host' => "www.163.com",
            "User-Agent" => 'Chrome/49.0.2587.3',
            'Accept' => 'text/html,application/xhtml+xml,application/xml',
            'Accept-Encoding' => 'gzip',
        ]);
        $ret = $cli->get('/');
        $chan->push(['www.163.com' => $cli->body]);
    });

    $result = [];
    for ($i = 0; $i < 2; $i++)
    {
        $result += $chan->pop();
    }
})

需要并发两个http请求,可使用go函数创建2个子协程,并发地请求多个URL 并创建了一个chan,使用use闭包引用语法,传递给子协程 主协程循环调用chan->pop,等待子协程完成任务,yield进入挂起状态 并发的两个子协程其中某个完成请求时,调用chan->push将数据推送给主协程

以上两种方法可以实现类似于go语言的并发调用

转载于:https://my.oschina.net/fage1151/blog/1838483

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值