您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么用PHP寫守護進程類”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么用PHP寫守護進程類”吧!
用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 = '/tmp/daemon_apc_keys'; /** * 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 = '/tmp/daemon.pid'; /** * processLocation * 進程信息記錄目錄 * * @var string */ public $processLocation = ''; /** * processHeartLocation * 進程心跳包文件 * * @var string */ public $processHeartLocation = ''; /** * Home path * * @var string * @since 1.0 */ public $homePath = '/'; /** * 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, 'releaseDaemon')); } /** * 啟動進程 * * @return bool */ public function main() { $this->_logMessage('Starting daemon'); if (!$this->_daemonize()) { $this->_logMessage('Could not start daemon', self::DLOG_ERROR); return false; } $this->_logMessage('Running...'); $this->_isRunning = true; while ($this->_isRunning) { $this->_doTask(); } return true; } /** * 停止進程 * * @return void */ public function stop() { $this->_logMessage('Stoping daemon'); $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't fork. Exiting. return false; } if (!$this->_setIdentity() && $this->requireSetIdentity) { // Required identity set failed. Exiting return false; } if (!posix_setsid()) { $this->_logMessage('Could not make the current process a session leader', self::DLOG_ERROR); return false; } if (!$fp = fopen($this->pidFileLocation, 'w')) { $this->_logMessage('Could not write to PID file', 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, 'sigHandler')); pcntl_signal(SIGTERM, array(&$this, 'sigHandler')); pcntl_signal(SIGUSR1, array(&$this, 'sigHandler')); pcntl_signal(SIGUSR2, array(&$this, 'sigHandler')); 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('Daemon already running with PID: '.$oldPid, (self::DLOG_TO_CONSOLE | self::DLOG_ERROR)); return true; } else { return false; } } /** * Forks process * * @return bool */ private function _fork() { $this->_logMessage('Forking...'); $pid = pcntl_fork(); if ($pid == -1) { // 出錯 $this->_logMessage('Could not fork', self::DLOG_ERROR); return false; } elseif ($pid) { // 父進程 $this->_logMessage('Killing parent'); 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('Could not set identity', 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('Shutdown signal'); exit(); break; case SIGCHLD: // Halt $this->_logMessage('Halt signal'); while (pcntl_waitpid(-1, $status, WNOHANG) > 0); break; case SIGUSR1: // User-defined $this->_logMessage('User-defined signal 1'); $this->_sigHandlerUser1(); break; case SIGUSR2: // User-defined $this->_logMessage('User-defined signal 2'); $this->_sigHandlerUser2(); break; } } /** * Signals handler: USR1 * 主要用于定時清理每個進程里被緩存的域名dns解析記錄 * * @return void */ protected function _sigHandlerUser1() { apc_clear_cache('user'); } /** * 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('Releasing daemon'); unlink($this->pidFileLocation); } } /** * writeProcess * 將當前進程信息寫入監控日志,另外的腳本會掃描監控日志的數據發送信號,如果沒有響應則重啟進程 * * @return void */ public function writeProcess() { // 初始化 proc $this->_initProcessLocation(); $command = trim(implode(' ', $_SERVER['argv'])); // 指定進程的目錄 $processDir = $this->processLocation . '/' . $this->_pid; $processCmdFile = $processDir . '/cmd'; $processPwdFile = $processDir . '/pwd'; // 所有進程所在的目錄 if (!is_dir($this->processLocation)) { mkdir($this->processLocation, 0777); chmod($processDir, 0777); } // 查詢重復的進程記錄 $pDirObject = dir($this->processLocation); while ($pDirObject && (($pid = $pDirObject->read()) !== false)) { if ($pid == '.' || $pid == '..' || intval($pid) != $pid) { continue; } $pDir = $this->processLocation . '/' . $pid; $pCmdFile = $pDir . '/cmd'; $pPwdFile = $pDir . '/pwd'; $pHeartFile = $pDir . '/heart'; // 根據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['PWD']); // 寫文件有緩存 usleep(1000); return true; } /** * _initProcessLocation * 初始化 * * @return void */ protected function _initProcessLocation() { $this->processLocation = ROOT_PATH . '/app/data/proc'; $this->processHeartLocation = $this->processLocation . '/' . $this->_pid . '/heart'; } }
到此,相信大家對“怎么用PHP寫守護進程類”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。