php

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
}

属性

运行速度

<?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
上一篇下一篇

猜你喜欢

热点阅读