Process [编辑本页]

swoole-1.7.2增加了一个进程管理模块,用来替代PHP的pcntl扩展。

需要注意Process进程在系统是非常昂贵的资源,创建进程消耗很大。另外创建的进程过多会导致进程切换开销大幅上升。可以使用vmstat指令查看操作系统每秒进程切换的次数。

vmstat 1 1000
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 8250028 509872 4061168    0    0    10    13   88   86  1  0 99  0  0
 0  0      0 8249532 509872 4061936    0    0     0     0  451 1108  0  0 100  0  0
 0  0      0 8249532 509872 4061884    0    0     0     0  684 1855  1  3 95  0  0
 0  0      0 8249532 509880 4061876    0    0     0    16  492 1332  0  0 99  0  0
 0  0      0 8249532 509880 4061844    0    0     0     0  379  893  0  0 100  0  0
 0  0      0 8249532 509880 4061844    0    0     0     0  440 1116  0  0 99  0  0

PHP自带的pcntl,存在很多不足,如

  • pcntl没有提供进程间通信的功能
  • pcntl不支持重定向标准输入和输出
  • pcntl只提供了fork这样原始的接口,容易使用错误
  • swoole_process提供了比pcntl更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。

swoole_process提供了如下特性:

  • swoole_process提供了基于unixsock的进程间通信,使用很简单只需调用write/read或者push/pop即可
  • swoole_process支持重定向标准输入和输出,在子进程内echo不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据
  • 配合swoole_event模块,创建的PHP子进程可以异步的事件驱动模式
  • swoole_process提供了exec接口,创建的进程可以执行其他程序,与原PHP父进程之间可以方便的通信

一个同步实例:

  • 子进程异常退出时,自动重启
  • 主进程异常退出时,子进程在干完手头活后退出
(new class{
    public $mpid=0;
    public $works=[];
    public $max_precess=1;
    public $new_index=0;

    public function __construct(){
        try {
            swoole_set_process_name(sprintf('php-ps:%s', 'master'));
            $this->mpid = posix_getpid();
            $this->run();
            $this->processWait();
        }catch (\Exception $e){
            die('ALL ERROR: '.$e->getMessage());
        }
    }

    public function run(){
        for ($i=0; $i < $this->max_precess; $i++) {
            $this->CreateProcess();
        }
    }

    public function CreateProcess($index=null){
        $process = new swoole_process(function(swoole_process $worker)use($index){
            if(is_null($index)){
                $index=$this->new_index;
                $this->new_index++;
            }
            swoole_set_process_name(sprintf('php-ps:%s',$index));
            for ($j = 0; $j < 16000; $j++) {
                $this->checkMpid($worker);
                echo "msg: {$j}\n";
                sleep(1);
            }
        }, false, false);
        $pid=$process->start();
        $this->works[$index]=$pid;
        return $pid;
    }
    public function checkMpid(&$worker){
        if(!swoole_process::kill($this->mpid,0)){
            $worker->exit();
            // 这句提示,实际是看不到的.需要写到日志中
            echo "Master process exited, I [{$worker['pid']}] also quit\n";
        }
    }

    public function rebootProcess($ret){
        $pid=$ret['pid'];
        $index=array_search($pid, $this->works);
        if($index!==false){
            $index=intval($index);
            $new_pid=$this->CreateProcess($index);
            echo "rebootProcess: {$index}={$new_pid} Done\n";
            return;
        }
        throw new \Exception('rebootProcess Error: no pid');
    }

    public function processWait(){
        while(1) {
            if(count($this->works)){
                $ret = swoole_process::wait();
                if ($ret) {
                    $this->rebootProcess($ret);
                }
            }else{
                break;
            }
        }
    }
});

  • 这个问题么

    请问怎么调用和使用,文档看的不是十分明白。有相关的demo么,还须要配置服务器么,

  • test

    1.7的时候,还可在创建的进程里调用task,1.8就不行了,报错 Warning: swoole_server::task(): The method can only be used in the worker process.

  • 不吃饭

    根本不支持fpm好吧:swoole_process must run at php_cli environment.

  • lockfan

    1.7.x是可以的,1.8为什么取消了还不知道,文档里也没说

  • Jetbrains

    请问为什swoole_timer_tick在process里边时间设置不准确呢

  • 俩鱼

    你好,我使用1.8.*是支持在fpm下运行的。

  • 俩鱼

    相信swoole扩展是很强大的。用纯C写的,运行效率一定会高。但文档写得不太严谨。并且文档中有多的感情色彩,不够理性!比如会出现一些字眼:完美、无缝、强悍、易用、轻松、方便。最好也别在技术文档中批评别人家东西。要矜持…[挖鼻屎]

  • 俩鱼

    创建一个子进程,通过exec方法去调用一个python脚本。如果python脚本中有raw_input()方法时,主进程无法通过管理获取数据。在centos的cli, fpm模式下都不可以。用proc_open调用此python脚本就可以通过pipe获取到数据。不知是哪里使用不当!

  • 俩鱼

    不好意思,打错字了。是“主进程无法通过管道获取数据”。

  • 刘高云

    这个实例中子进程内将最大子进程数设置多1个时, $this->new_index++语句未生效,一直未初始值0,求大神解释?

  • 刘高云

    这个是多进程程序吧,父进程和子进程有自己的数据空间和代码空间,即便使用匿名函数传递参数,子进程也只是获得父进程的副本,子进程的数据空间的修改对父进程的数据空间并不会造成改变。应该使用swoole_table共享空间来做这个实例吧

  • 刘高云

    官方给的示例,父子进程的数据交换不正确,父子进程各有各的数据空间,改变某个进程中成员变量的值并不能影响其他进程的数据空间,不可以。下面是我使用共享内容修改后的示例 <?php class MainProcess{ public $mpid = 0; public $works = []; public $max_process = 5; public static $swoole_table = NULL;

    public function __construct(){
        try{
            self::$swoole_table = new swoole_table(1024);
            self::$swoole_table->column('index', swoole_table::TYPE_INT);//用于父子进程间数据交换
            self::$swoole_table->create();
            swoole_set_process_name('php-ps:master');
            $this->mpid = posix_getpid();
            $this->run();
            $this->processWait();
        }catch(\Exception $e){
            die('ALL ERROR:'.$e->getMessage());
        }
    }
    
    public function run(){
        for($i=0;$i < $this->max_process; $i++){
            $this->createProcess();
        }
    }
    public function createProcess($index=null){
        $process = new swoole_process(function(swoole_process $worker) use ($index) {
            if(is_null($index)){//如果没有指定了索引,新建的子进程,开启计数
                $index=self::$swoole_table->get('index');
                if($index === false){
                    $index = 0;
                }else{
                    $index++;
                }
            }
    
            self::$swoole_table->set('index',array('index'=>$index));
            swoole_set_process_name("php-ps:{$index}");
            for($j=0;$j<16000;$j++){
                $this->checkMpid($worker);
                echo "msg:{$j}\n";
                sleep(1);
            }
        },false, false);
        $pid = $process->start();
        $index=self::$swoole_table->get('index');
        $index=$index['index'];
        $this->works[$index] = $pid;
        return $pid;
    }
    
    public function checkMpid(&$worker){
        if(!swoole_process::kill($this->mpid,0)){ //import! check whether master process is running  
            $worker->exit();
            file_put_contents('/tmp/runtime.log', "Master process exited, I [{$worker['pid']}] also quit\n", FILE_APPEND);
        }
    }
    
    public function rebootProcess($res){
    
        $pid = $ret['pid'];
        $index = array_search($pid, $this->works);
        if($index!==false){
            $index = intval($index);
            $new_pid = $this->createProcess($index);
            echo "rebootProcess:{$index}={$new_pid} Done\n";
            return;
        }
    
        throw new \Exception("rebootProcess Error: no pid");
    }
    
    public function processWait(){
        while(1){
            if(count($this->works)){//$result = array('code' => 0, 'pid' => 15001, 'signal' => 15);
                $ret = swoole_process::wait();
            }else{
                $this->rebootProcess($ret);
            }
        }
    }
    

    }

    new MainProcess();

  • 何曦

    process打错了,打成了precess