php教程 PHP源码 PHP 데몬 클래스

PHP 데몬 클래스

Nov 08, 2016 pm 05:37 PM

PHP로 구현된 데몬 클래스. 서버의 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으로 문의하세요.

뜨거운 기사 태그

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)