温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么用PHP写守护进程类

发布时间:2021-09-03 22:26:03 来源:亿速云 阅读:542 作者:chen 栏目:开发技术

本篇内容主要讲解“怎么用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写守护进程类”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

php
AI