搜尋
首頁php教程PHP源码PHP 守護程式類
PHP 守護程式類Nov 08, 2016 pm 05:37 PM

用 PHP 實作的 Daemon 類別。可以在伺服器上實作佇列或脫離 crontab 的排程任務。 

使用的時候,繼承於這個類,並重寫 _doTask 方法,透過 main 初始化執行。

<?php
 
class Daemon {
 
    const DLOG_TO_CONSOLE = 1;
    const DLOG_NOTICE = 2;
    const DLOG_WARNING = 4;
    const DLOG_ERROR = 8;
    const DLOG_CRITICAL = 16;
 
    const DAPC_PATH =  &#39;/tmp/daemon_apc_keys&#39;;
 
    /**
     * User ID
     *
     * @var int
     */
    public $userID = 65534; // nobody
 
    /**
     * Group ID
     *
     * @var integer
     */
    public $groupID = 65533; // nobody
 
    /**
     * Terminate daemon when set identity failure ?
     *
     * @var bool
     * @since 1.0.3
     */
    public $requireSetIdentity = false;
 
    /**
     * Path to PID file
     *
     * @var string
     * @since 1.0.1
     */
    public $pidFileLocation = &#39;/tmp/daemon.pid&#39;;
 
    /**
     * processLocation
     * 进程信息记录目录
     *
     * @var string
     */
    public $processLocation = &#39;&#39;;
 
    /**
     * processHeartLocation
     * 进程心跳包文件
     *
     * @var string
     */
    public $processHeartLocation = &#39;&#39;;
 
    /**
     * Home path
     *
     * @var string
     * @since 1.0
     */
    public $homePath = &#39;/&#39;;
 
    /**
     * Current process ID
     *
     * @var int
     * @since 1.0
     */
    protected $_pid = 0;
 
    /**
     * Is this process a children
     *
     * @var boolean
     * @since 1.0
     */
    protected $_isChildren = false;
 
    /**
     * Is daemon running
     *
     * @var boolean
     * @since 1.0
     */
    protected $_isRunning = false;
 
    /**
     * Constructor
     *
     * @return void
     */
    public function __construct() {
 
        error_reporting(0);
        set_time_limit(0);
        ob_implicit_flush();
 
        register_shutdown_function(array(&$this, &#39;releaseDaemon&#39;));
    }
 
    /**
     * 启动进程
     *
     * @return bool
     */
    public function main() {
 
        $this->_logMessage(&#39;Starting daemon&#39;);
 
        if (!$this->_daemonize()) {
            $this->_logMessage(&#39;Could not start daemon&#39;, self::DLOG_ERROR);
 
            return false;
        }
 
        $this->_logMessage(&#39;Running...&#39;);
 
        $this->_isRunning = true;
 
        while ($this->_isRunning) {
            $this->_doTask();
        }
 
        return true;
    }
 
    /**
     * 停止进程
     *
     * @return void
     */
    public function stop() {
 
        $this->_logMessage(&#39;Stoping daemon&#39;);
 
        $this->_isRunning = false;
    }
 
    /**
     * Do task
     *
     * @return void
     */
    protected function _doTask() {
        // override this method
    }
 
    /**
     * _logMessage
     * 记录日志
     *
     * @param string 消息
     * @param integer 级别
     * @return void
     */
    protected function _logMessage($msg, $level = self::DLOG_NOTICE) {
        // override this method
    }
 
    /**
     * Daemonize
     *
     * Several rules or characteristics that most daemons possess:
     * 1) Check is daemon already running
     * 2) Fork child process
     * 3) Sets identity
     * 4) Make current process a session laeder
     * 5) Write process ID to file
     * 6) Change home path
     * 7) umask(0)
     *
     * @access private
     * @since 1.0
     * @return void
     */
    private function _daemonize() {
 
        ob_end_flush();
 
        if ($this->_isDaemonRunning()) {
            // Deamon is already running. Exiting
            return false;
        }
 
        if (!$this->_fork()) {
            // Coudn&#39;t fork. Exiting.
            return false;
        }
 
        if (!$this->_setIdentity() && $this->requireSetIdentity) {
            // Required identity set failed. Exiting
            return false;
        }
 
        if (!posix_setsid()) {
            $this->_logMessage(&#39;Could not make the current process a session leader&#39;, self::DLOG_ERROR);
 
            return false;
        }
 
        if (!$fp = fopen($this->pidFileLocation, &#39;w&#39;)) {
            $this->_logMessage(&#39;Could not write to PID file&#39;, self::DLOG_ERROR);
            return false;
        } else {
            fputs($fp, $this->_pid);
            fclose($fp);
        }
 
        // 写入监控日志
        $this->writeProcess();
 
        chdir($this->homePath);
        umask(0);
 
        declare(ticks = 1);
 
        pcntl_signal(SIGCHLD, array(&$this, &#39;sigHandler&#39;));
        pcntl_signal(SIGTERM, array(&$this, &#39;sigHandler&#39;));
        pcntl_signal(SIGUSR1, array(&$this, &#39;sigHandler&#39;));
        pcntl_signal(SIGUSR2, array(&$this, &#39;sigHandler&#39;));
 
        return true;
    }
 
    /**
     * Cheks is daemon already running
     *
     * @return bool
     */
    private function _isDaemonRunning() {
 
        $oldPid = file_get_contents($this->pidFileLocation);
 
        if ($oldPid !== false && posix_kill(trim($oldPid),0))
        {
            $this->_logMessage(&#39;Daemon already running with PID: &#39;.$oldPid, (self::DLOG_TO_CONSOLE | self::DLOG_ERROR));
 
            return true;
        }
        else
        {
            return false;
        }
    }
 
    /**
     * Forks process
     *
     * @return bool
     */
    private function _fork() {
 
        $this->_logMessage(&#39;Forking...&#39;);
 
        $pid = pcntl_fork();
 
        if ($pid == -1) {
            // 出错
            $this->_logMessage(&#39;Could not fork&#39;, self::DLOG_ERROR);
 
            return false;
        } elseif ($pid) {
            // 父进程
            $this->_logMessage(&#39;Killing parent&#39;);
 
            exit();
        } else {
            // fork的子进程
            $this->_isChildren = true;
            $this->_pid = posix_getpid();
 
            return true;
        }
    }
 
    /**
     * Sets identity of a daemon and returns result
     *
     * @return bool
     */
    private function _setIdentity() {
 
        if (!posix_setgid($this->groupID) || !posix_setuid($this->userID))
        {
            $this->_logMessage(&#39;Could not set identity&#39;, self::DLOG_WARNING);
 
            return false;
        }
        else
        {
            return true;
        }
    }
 
    /**
     * Signals handler
     *
     * @access public
     * @since 1.0
     * @return void
     */
    public function sigHandler($sigNo) {
 
        switch ($sigNo)
        {
            case SIGTERM:   // Shutdown
                $this->_logMessage(&#39;Shutdown signal&#39;);
                exit();
                break;
 
            case SIGCHLD:   // Halt
                $this->_logMessage(&#39;Halt signal&#39;);
                while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
                break;
            case SIGUSR1:   // User-defined
                $this->_logMessage(&#39;User-defined signal 1&#39;);
                $this->_sigHandlerUser1();
                break;
            case SIGUSR2:   // User-defined
                $this->_logMessage(&#39;User-defined signal 2&#39;);
                $this->_sigHandlerUser2();
                break;
        }
    }
 
    /**
     * Signals handler: USR1
     *  主要用于定时清理每个进程里被缓存的域名dns解析记录
     *
     * @return void
     */
    protected function _sigHandlerUser1() {
        apc_clear_cache(&#39;user&#39;);
    }
 
    /**
     * Signals handler: USR2
     * 用于写入心跳包文件
     *
     * @return void
     */
    protected function _sigHandlerUser2() {
 
        $this->_initProcessLocation();
 
        file_put_contents($this->processHeartLocation, time());
 
        return true;
    }
 
    /**
     * Releases daemon pid file
     * This method is called on exit (destructor like)
     *
     * @return void
     */
    public function releaseDaemon() {
 
        if ($this->_isChildren && is_file($this->pidFileLocation)) {
            $this->_logMessage(&#39;Releasing daemon&#39;);
 
            unlink($this->pidFileLocation);
        }
    }
 
    /**
     * writeProcess
     * 将当前进程信息写入监控日志,另外的脚本会扫描监控日志的数据发送信号,如果没有响应则重启进程
     *
     * @return void
     */
    public function writeProcess() {
 
        // 初始化 proc
        $this->_initProcessLocation();
 
        $command = trim(implode(&#39; &#39;, $_SERVER[&#39;argv&#39;]));
 
        // 指定进程的目录
        $processDir = $this->processLocation . &#39;/&#39; . $this->_pid;
        $processCmdFile = $processDir . &#39;/cmd&#39;;
        $processPwdFile = $processDir . &#39;/pwd&#39;;
 
        // 所有进程所在的目录
        if (!is_dir($this->processLocation)) {
            mkdir($this->processLocation, 0777);
            chmod($processDir, 0777);
        }
 
        // 查询重复的进程记录
        $pDirObject = dir($this->processLocation);
        while ($pDirObject && (($pid = $pDirObject->read()) !== false)) {
            if ($pid == &#39;.&#39; || $pid == &#39;..&#39; || intval($pid) != $pid) {
                continue;
            }
 
            $pDir = $this->processLocation . &#39;/&#39; . $pid;
            $pCmdFile = $pDir . &#39;/cmd&#39;;
            $pPwdFile = $pDir . &#39;/pwd&#39;;
            $pHeartFile = $pDir . &#39;/heart&#39;;
 
            // 根据cmd检查启动相同参数的进程
            if (is_file($pCmdFile) && trim(file_get_contents($pCmdFile)) == $command) {
                unlink($pCmdFile);
                unlink($pPwdFile);
                unlink($pHeartFile);
 
                // 删目录有缓存
                usleep(1000);
 
                rmdir($pDir);
            }
        }
 
        // 新进程目录
        if (!is_dir($processDir)) {
            mkdir($processDir, 0777);
            chmod($processDir, 0777);
        }
 
        // 写入命令参数
        file_put_contents($processCmdFile, $command);
        file_put_contents($processPwdFile, $_SERVER[&#39;PWD&#39;]);
 
        // 写文件有缓存
        usleep(1000);
 
        return true;
    }
 
    /**
     * _initProcessLocation
     * 初始化
     *
     * @return void
     */
    protected function _initProcessLocation() {
 
        $this->processLocation = ROOT_PATH . &#39;/app/data/proc&#39;;
        $this->processHeartLocation = $this->processLocation . &#39;/&#39; . $this->_pid . &#39;/heart&#39;;
    }
}


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版