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);
            }
        });
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM