资讯专栏INFORMATION COLUMN

PHP 多进程系列笔记(三)

focusj / 2429人阅读

摘要:本节讲解几个多进程的实例。新开终端,我们使用命令查看进程可以看到个进程个主进程,个子进程。使用命令结束子进程,主进程会重新拉起一个新的子进程。

本节讲解几个多进程的实例。

多进程实例 Master-Worker结构

下面例子实现了简单的多进程管理:

支持设置最大子进程数

Master-Worker结构:Worker挂掉,Master进程会重新创建一个

 0){
    exit;//父进程退出
}else{
    // 从当前终端分离
    if (posix_setsid() == -1) {
        die("could not detach from terminal");
    }

    $id = getmypid();   
    echo time()." Master process, pid {$id}
"; 

    for($i=0; $i<$MAX_PROCESS;$i++){
        start_worker_process();
    }

    //Master进程等待子进程退出,必须是死循环
    while(1){
        foreach($pids as $pid){
            if($pid){
                $res = pcntl_waitpid($pid, $status, WNOHANG);
                if ( $res == -1 || $res > 0 ){
                    echo time()." Worker process $pid exit, will start new... 
";
                    start_worker_process();
                    unset($pids[$pid]);
                }
            }
        }
    }
}

/**
 * 创建worker进程
 */
function start_worker_process(){
    global $pids;
    $pid = pcntl_fork();
    if($pid <0){
        exit("fork fail
");
    }elseif($pid > 0){
        $pids[$pid] = $pid;
        // exit; //此处不可退出,否则Master进程就退出了
    }else{
        //实际代码
        $id = getmypid();   
        $rand = rand(1,3);
        echo time()." Worker process, pid {$id}. run $rand s
"; 
        while(1){
            sleep($rand);
        }
    }
}

~~~防盗版声明:本文系原创文章,发布于公众号飞鸿影的博客(fhyblog)及博客园,转载需作者同意。~~~

多进程Server

下面我们使用多进程实现一个tcp服务器,支持:

多进程处理客户端连接

子进程退出,Master进程会重新创建一个

支持事件回调

 0){
            exit;//父进程退出
        } else{
            // 从当前终端分离
            if (posix_setsid() == -1) {
                die("could not detach from terminal");
            }

            umask(0);

            $id = getmypid();   
            echo time()." Master process, pid {$id}
"; 

            //创建tcp server
            $this->socket = stream_socket_server("tcp://0.0.0.0:9201", $errno, $errstr);
            if(!$this->socket) exit("start server err: $errstr --- $errno");
        }
    }

    public function run(){
        for($i=0; $istart_worker_process();
        }

        echo "waiting client...
";

        //Master进程等待子进程退出,必须是死循环
        while(1){
            foreach($this->pids as $k=>$pid){
                if($pid){
                    $res = pcntl_waitpid($pid, $status, WNOHANG);
                    if ( $res == -1 || $res > 0 ){
                        echo time()." Worker process $pid exit, will start new... 
";
                        $this->start_worker_process();
                        unset($this->pids[$k]);
                    }
                }
            }
            sleep(1);//让出1s时间给CPU
        }
    }

    /**
     * 创建worker进程,接受客户端连接
     */
    private function start_worker_process(){
        $pid = pcntl_fork();
        if($pid <0){
            exit("fork fail
");
        }elseif($pid > 0){
            $this->pids[] = $pid;
            // exit; //此处不可退出,否则Master进程就退出了
        }else{
            $this->acceptClient();
        }
    }

    private function acceptClient()
    {
        //子进程一直等待客户端连接,不能退出
        while(1){
            $conn = stream_socket_accept($this->socket, -1);
            if($this->onConnect) call_user_func($this->onConnect, $conn); //回调连接事件

            //开始循环读取消息
            $recv = ""; //实际收到消息
            $buffer = ""; //缓冲消息
            while(1){
                $buffer = fread($conn, 20);

                //没有收到正常消息
                if($buffer === false || $buffer === ""){
                    if($this->onClose) call_user_func($this->onClose, $conn); //回调断开连接事件
                    break;//结束读取消息,等待下一个客户端连接
                }

                $pos = strpos($buffer, "
"); //消息结束符
                if($pos === false){
                    $recv .= $buffer;                            
                }else{
                    $recv .= trim(substr($buffer, 0, $pos+1));

                    if($this->onMessage) call_user_func($this->onMessage, $conn, $recv); //回调收到消息事件

                    //客户端强制关闭连接
                    if($recv == "quit"){
                        echo "client close conn
";
                        fclose($conn);
                        break;
                    }

                    $recv = ""; //清空消息,准备下一次接收
                }
            }
        }
    }

    function __destruct() {
        @fclose($this->socket);
    }
}

$server =  new TcpServer();

$server->onConnect = function($conn){
    echo "onConnect -- accepted " . stream_socket_get_name($conn,true) . "
";
    fwrite($conn,"conn success
");
};

$server->onMessage = function($conn,$msg){
    echo "onMessage --" . $msg . "
";
    fwrite($conn,"received ".$msg."
");
};

$server->onClose = function($conn){
    echo "onClose --" . stream_socket_get_name($conn,true) . "
";
    fwrite($conn,"onClose "."
");
};

$server->run();

运行:

$ php process_multi.server.php 
1528734803 Master process, pid 9110
waiting client...

此时服务端已经变成守护进程了。新开终端,我们使用ps命令查看进程:

$ ps -ef | grep php
yjc       9110     1  0 00:33 ?        00:00:00 php process_multi.server.php
yjc       9111  9110  0 00:33 ?        00:00:00 php process_multi.server.php
yjc       9112  9110  0 00:33 ?        00:00:00 php process_multi.server.php
yjc       9113  9110  0 00:33 ?        00:00:00 php process_multi.server.php
yjc       9134  8589  0 00:35 pts/1    00:00:00 grep php

可以看到4个进程:1个主进程,3个子进程。使用kill命令结束子进程,主进程会重新拉起一个新的子进程。

然后我们使用telnet测试连接:

$ telnet 127.0.0.1 9201
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is "^]".
conn success
hello server!
received hello server!
quit
received quit
Connection closed by foreign host.

欢迎关注公众号及时获取最新文章推送!


推荐!每月仅需$2.5,即可拥有配置SSD的VPS!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28908.html

相关文章

  • PHP进程系列笔记(一)

    摘要:用于创建子进程。该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。注意处需要注意子进程需要防止子进程也进入循环。如果没有,最终创建的子进程不只个。 本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识。 PCNTL在PHP中进程控制支持默认是关闭的。您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或CLI版本...

    ddongjian0000 评论0 收藏0
  • PHP进程系列笔记(二)

    摘要:任何进程在退出前使用退出都会变成僵尸进程用于保存进程的状态等信息,然后由进程接管。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。那么子进程结束后,没有回收,就产生僵尸进程了。本小节我们通过安装信号处理函数来解决僵尸进程问题。 上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识。 僵尸(zombie)进程 这里说下僵尸进程...

    CatalpaFlat 评论0 收藏0
  • PHP进程系列笔记(五)

    摘要:消息队列更常见的用途是主进程分配任务,子进程消费执行。子进程前面加了个,这是为了防止父进程还未往消息队列中加入内容直接退出。 前面几节都是讲解pcntl扩展实现的多进程程序。本节给大家介绍swoole扩展的swoole_process模块。 swoole多进程 swoole_process 是swoole提供的进程管理模块,用来替代PHP的pcntl扩展。 首先,确保安装的swoole...

    qianfeng 评论0 收藏0
  • PHP进程系列笔记(四)

    摘要:本节主要讲解常用函数和进程池的概念,也会涉及到守护进程的知识。所以任何时候,建议预先创建好进程,也就是使用进程池的方式实现。 本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识。本节难度较低。 Posix常用函数 posix_kill 向指定pid进程发送信号。成功时返回 TRUE , 或者在失败时返回 FALSE 。 bool posix_kill ( int $...

    Cc_2011 评论0 收藏0
  • php创建恒定数量的进程(pcntl学习笔记

    摘要:最近在学习进程相关的知识,配合实际编码,觉得有点意思。闲话不说了,进入正题,受网友启发,如何创建恒定数量的进程呢有进程挂了,就创建新的,进程,进程多了就杀掉几个。 最近在学习进程相关的知识,配合实际编码,觉得有点意思。这段时间工作不忙,有时间自己研究进步,感觉这才是真正的工作啊。相比上一家公司(压抑的工作饱和度……)感觉开心很多。下一步再研究一下多线程。看看能不能插入一千万条数据。闲话...

    hizengzeng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<