swoole(5)信号监听、热重启

一:信号监听

信号:由用户、系统或者进程发给目标进程的信息,以通知目标进程某个状态的改变或系统异常

信号查看:kill -l 

SIGHUP     终止进程     终端线路挂断
SIGINT     终止进程     中断进程
SIGKILL     终止进程       杀死进程
SIGPIPE     终止进程      向一个没有读进程的管道写数据
SIGALARM    终止进程      计时器到时
SIGTERM     终止进程      软件终止信号
SIGSTOP     停止进程     非终端来的停止信号
SIGTSTP     停止进程      终端来的停止信号
SIGCONT     忽略信号     继续执行一个停止的进程
SIGURG      忽略信号      I/O紧急信号
SIGIO      忽略信号     描述符上可以进行I/O
SIGPROF     终止进程     统计分布图用计时器到时
SIGUSR1    终止进程      用户定义信号1
SIGUSR2    终止进程       用户定义信号2
SIGVTALRM   终止进程       虚拟计时器到时

swoole热重启命令:

1、kill -SIGTERM|-15 master_pid  终止Swoole程序,一种优雅的终止信号,会待进程执行完当前程序之后中断,而不是直接干掉进程
2、kill -USR1|-10  master_pid  重启所有的Worker进程
3、kill -USR2|-12  master_pid   重启所有的Task Worker进程  

重启子进程、拉起子进程代码:

<?php
class Worker {
    //监听socket
    protected $socket = NULL;
    //连接事件回调
    public $onConnect = NULL;
    //接收消息事件回调
    public $onMessage = NULL;
    public $workerNum = 4;
    public $addr;
    public $worker_pid;
    public $master_pid;

    public function __construct($socket_address) {
       $this->addr=$socket_address;
       $this->master_pid = posix_getpid();
    }

    //创建子进程
    public function fork($worker_num) {
        for ($i = 0; $i < $worker_num; $i++) {
            $pid = pcntl_fork();
            if ($pid < 0) {
                exit('创建失败');
            } else if ($pid > 0) {
                //存储子进程id
                $this->worker_pid[]=$pid;
            } else {
                $this->accept();
                exit();
            }
        }
    }

    public function accept() {
        $opts = array(
            'socket' => array(
                'backlog' => '10240',
            ),
        );

        $context = stream_context_create($opts);

        stream_context_set_option($context,'socket','so_reuseport',1);

        $this->socket = stream_socket_server($this->addr,$error,$errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,$context);
        swoole_event_add($this->socket, function ($fd) {
            //服务端接收客户端请求
            $clientSocket = stream_socket_accept($this->socket);
            if (!empty($clientSocket) && is_callable($this->onConnect)) {
                call_user_func($this->onConnect, $clientSocket);
            }
            swoole_event_add($clientSocket, function ($fd) {
                $buffer = fread($fd, 65535);
                //如果数据为空,或者为false,不是资源类型
                if (empty($buffer)) {
                    if (feof($fd) || !is_resource($fd)) {
                        //触发关闭事件
                        fclose($fd);
                    }
                }
                if (!empty($buffer) && is_callable($this->onMessage)) {
                    call_user_func($this->onMessage, $fd, $buffer);
                }
            });
        });
    }
    /**
     * 捕获信号
     * 监视worker进程.拉起进程
     */
    public function monitorWorkers(){
        //注册信号事件回调,是不会自动执行的
        pcntl_signal(SIGUSR1, array($this, 'signalHandler'),false); //重启woker进程信号

//        pcntl_signal(SIGUSR1,array($this,'signalHandler'),false); //重启worker进程信号
        $status = 0;
        //回收子进程
       while(1){
           //reload
           // 当发现信号队列,一旦发现有信号就会触发进程绑定事件回调
           pcntl_signal_dispatch();
            $pid = pcntl_wait($status); //当信号到达之后就会被中断
           //ctrl+c
           //如果进程不是正常情况下的退出,重启子进程,我想要维持子进程个数
            if($pid>1 && $pid != $this->master_pid  && !pcntl_wifexited($status)){
                     $index=array_search($pid,$this->worker_pid);
                     $this->fork(1);
                     var_dump('拉起子进程');
                     unset($this->worker_pid[$index]);
            }
           pcntl_signal_dispatch();
           //进程重启的过程当中会有新的信号过来,如果没有调用pcntl_signal_dispatch,信号不会被处理
        }
    }

    public function signalHandler($sigo){
        switch ($sigo){
            case SIGUSR1:
                $this->reload();
                echo '收到重启信号';
                break;
        }
    }

    public function reload(){
        foreach($this->worker_pid as $index =>$pid){
            posix_kill($pid,SIGKILL);
            var_dump("杀掉的子进程$pid");
            unset($this->worker_pid[$index]);
            $this->fork(1);
        }
    }

    public function start() {
        $this->fork($this->workerNum);
        $this->monitorWorkers(); //监视程序,捕获信号,监视worker进程

    }
}


$worker = new Worker('tcp://0.0.0.0:9801');

$worker->onConnect = function ($args) {
    echo "新的连接来了.{$args}.PHP_EOL";
};
$worker->onMessage = function ($conn, $message) {
//    var_dump($conn, $message);
    $content = "hello word qwe";
    $http_resonse = "HTTP/1.1 200 OK\r\n";
    $http_resonse .= "Content-Type: text/html;charset=UTF-8\r\n";
    $http_resonse .= "Connection: keep-alive\r\n";
    $http_resonse .= "Server: php socket server\r\n";
    $http_resonse .= "Content-length: " . strlen($content) . "\r\n\r\n";
    $http_resonse .= $content;
    fwrite($conn, $http_resonse);
};
$worker->start();

cli运行:

 

 二:inotify热重启

inotify:是linux内核提供的一组系统调用,她可以监控文件系统操作,比如文件或者目录的创建、读取、写入、权限修改、删除等

    public function start() {
        //获取配置文件
        $this->watch();
        $this->fork($this->workerNum);
        $this->monitorWorkers(); //监视程序,捕获信号,监视worker进程
    }

    /**
     * 文件监视,自动重启
     */
    protected  function watch(){
        $init=inotify_init(); //初始化
        $files=get_included_files();
        foreach ($files as $file){
            inotify_add_watch($init,$file,IN_MODIFY); //监视相关的文件
        }
        //监听
        swoole_event_add($init,function ($fd){
            $events=inotify_read($fd);
            if(!empty($events)){
                posix_kill($this->master_pid,SIGUSR1);
            }
        });
    }
posted @ 2020-03-05 17:26  花花妹子。  阅读(1410)  评论(0编辑  收藏  举报