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有:
- Swoole\Coroutine\Client
- Swoole\Coroutine\Redis
- Swoole\Coroutine\MySQL
- 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语言的并发调用