Swoole多进程
2019-06-22 本文已影响0人
JunChow520
Swoole Server和Nginx服务一样,采用了多进程的架构模型。相对比多线程模型,多进程结构管理方便,不存在线程冲突和线程安全问题,代码编写相对简单,缺点在于进程和进程之间的通信没有多线程模型直接,存在一定的损耗。另外,PHP开发依赖的PHP-FPM也是多进程模型,因此为了减少学习成本降低开发难度,Swoole最终选择了多进程架构。
创建多进程
$ vim test.php
<?php
$worker_num = 2;
for($i=0; $i<$worker_num; $i++){
sleep(1);
$process = new swoole_process(function(swoole_process $worker){
var_dump($worker);
});
$pid = $process->start();
echo "pid = {$pid}".PHP_EOL;
}
执行脚本
$ php test.php
object(Swoole\Process)#1 (6) {
["pipe"]=>int(4)
["callback"]=>NULL
["msgQueueId"]=>NULL
["msgQueueKey"]=>NULL
["pid"]=>int(287)
["id"]=>NULL
}
object(Swoole\Process)#3 (6) {
["pipe"]=>int(6)
["callback"]=>NULL
["msgQueueId"]=>NULL
["msgQueueKey"]=>NULL
["pid"]=>int(288)
["id"]=>NULL
}
属性
-
pipe
进程的管道ID -
pid
当前子进程的PID -
callback
回调函数名称
运行速度
<?php
$begin_time = microtime(true);
$worker_num = 2;
for($i=0; $i<$worker_num; $i++){
$process = new swoole_process(function(swoole_process $worker) use($begin_time){
for($i=0; $i<100000000; $i++){
}
$spend_time = microtime(true) - $begin_time;
echo "spend time {$spend_time}s".PHP_EOL;
});
$pid = $process->start();
echo "pid = {$pid}".PHP_EOL;
}
root@539b086d15cc:/app# php test.php
pid = 296
pid = 297
root@539b086d15cc:/app#
spend time 7.8743269443512s
spend time 7.8978328704834s
例如:为多条Linux的Shell命令创建子进程来执行
$ vim command.php
使用多线程创建三个子进程执行命令
<?php
$begintime = microtime(true);
//子进程创建成功后要执行的函数
function handle(swoole_process $worker){
sleep(1);//暂停1秒
$cmd = $worker->read();
//开启页面缓存
ob_start();
passthru($cmd);//执行外部程序并显示未经处理的原始输出
$ret = ob_get_clean();
//设置默认值
$ret = empty($ret) ? "null" : $ret;
$worker->write($ret);
}
//多进程
$cmds = ["uname", "date", "whoami"];
foreach($cmds as $cmd){
//创建子进程
$process = new swoole_process("handle", true);
$process->start();
//通过管道发送数据到子进程
$process->write($cmd);//管道是单向的发出的 数据必须由另一端读取。
//同步阻塞读取管道数据
echo $process->read();//获取命令执行结果
}
//子进程结束必须执行wait进行回收,否则子进程会变成僵尸进程。
while($ret = swoole_process::wait()){
$pid = $ret["pid"];
echo "worker exit: pid={$pid}".PHP_EOL;
}
$endtime = microtime(true);
echo sprintf("spend time %.3f s\n", $endtime-$begintime);
执行脚本
$ php command.php
Linux
Sat Jun 22 16:49:48 CST 2019
root
worker exit: pid=232
worker exit: pid=235
worker exit: pid=238
spend time 3.066 s
脚本执行耗费的时间是3秒多,为什么使用多线程开启3个子线程执行,还是3秒呢?
原因是父进程读取子进程返回的数据时是同步阻塞读取管道数据的
echo $process->read();//获取命令执行结果
由于read
是同步阻塞读取,导致父进程依次等待每个子进程处理完毕后才返回内容,进入下一个循环。
解决方案:先不获取子进程返回值,等循环结束后统一返回。
<?php
$begintime = microtime(true);
//子进程创建成功后要执行的函数
function handle(swoole_process $worker){
sleep(1);//暂停1秒
$cmd = $worker->read();
//开启页面缓存
ob_start();
passthru($cmd);//执行外部程序并显示未经处理的原始输出
$ret = ob_get_clean();
//设置默认值
$ret = empty($ret) ? "null" : $ret;
$worker->write($ret);
}
//多进程
$pros = [];
$cmds = ["uname", "date", "whoami"];
foreach($cmds as $cmd){
//创建子进程
$process = new swoole_process("handle", true);
$process->start();
//通过管道发送数据到子进程
$process->write($cmd);//管道是单向的发出的 数据必须由另一端读取。
//先不获取子进程返回值等循环结束后统一返回
$pros[] = $process;
}
//统一返回子进程数据
foreach($pros as $item){
echo $item->read();
}
//子进程结束必须执行wait进行回收,否则子进程会变成僵尸进程。
while($ret = swoole_process::wait()){
$pid = $ret["pid"];
echo "worker exit: pid={$pid}".PHP_EOL;
}
$endtime = microtime(true);
echo sprintf("spend time %.3f s\n", $endtime-$begintime);
$ php command.php
Linux
Sat Jun 22 17:05:19 CST 2019
root
worker exit: pid=243
worker exit: pid=242
worker exit: pid=244
spend time 1.059 s
解决方案:使用swoole_event_add
将管道加入到事件循环中,使其变为异步模式。
<?php
$begintime = microtime(true);
//子进程创建成功后要执行的函数
function handle(swoole_process $worker){
sleep(1);//暂停1秒
$cmd = $worker->read();
//开启页面缓存
ob_start();
passthru($cmd);//执行外部程序并显示未经处理的原始输出
$ret = ob_get_clean();
//设置默认值
$ret = empty($ret) ? "null" : $ret;
$worker->write($ret);
}
//多进程
$cmds = ["uname", "date", "whoami"];
foreach($cmds as $cmd){
//创建子进程
$process = new swoole_process("handle", true);
$process->start();
//通过管道发送数据到子进程
$process->write($cmd);//管道是单向的发出的 数据必须由另一端读取。
//将管道加入到事件循环中使其转为异步模式
swoole_event_add($process->pipe, function($pipe) use($process){
echo $process->read();
});
}
//子进程结束必须执行wait进行回收,否则子进程会变成僵尸进程。
while($ret = swoole_process::wait()){
$pid = $ret["pid"];
echo "worker exit: pid={$pid}".PHP_EOL;
}
$endtime = microtime(true);
echo sprintf("spend time %.3f s\n", $endtime-$begintime);
$ php command.php
worker exit: pid=254
worker exit: pid=253
worker exit: pid=252
spend time 1.063 s
root
Sat Jun 22 17:08:57 CST 2019
Linux