zoukankan      html  css  js  c++  java
  • PHP多进程系列笔记(三)

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

    多进程实例

    Master-Worker结构

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

    • 支持设置最大子进程数
    • Master-Worker结构:Worker挂掉,Master进程会重新创建一个
    <?php 
    
    $pids = []; //存储子进程pid
    $MAX_PROCESS = 3;//最大进程数
    
    $pid = pcntl_fork();
    if($pid <0){
        exit("fork fail
    ");
    }elseif($pid > 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进程会重新创建一个
    • 支持事件回调
    <?php 
    
    class TcpServer{
        const MAX_PROCESS = 3;//最大进程数
        private $pids = []; //存储子进程pid
        private $socket;
    
        public function __construct(){
            $pid = pcntl_fork();
            if($pid <0){
                exit("fork fail
    ");
            }elseif($pid > 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; $i<self::MAX_PROCESS;$i++){
                $this->start_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.
    
  • 相关阅读:
    16. 3Sum Closest
    17. Letter Combinations of a Phone Number
    20. Valid Parentheses
    77. Combinations
    80. Remove Duplicates from Sorted Array II
    82. Remove Duplicates from Sorted List II
    88. Merge Sorted Array
    257. Binary Tree Paths
    225. Implement Stack using Queues
    113. Path Sum II
  • 原文地址:https://www.cnblogs.com/52fhy/p/9211505.html
Copyright © 2011-2022 走看看