分享PHP守护进程类
网络编程 2021-07-05 09:09www.168986.cn编程入门
这篇文章主要介绍了PHP守护进程类,守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。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 s() { $this->_logMessage('Sing 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) @aess 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); ptl_signal(SIGCHLD, array(&$this, 'sigHandler')); ptl_signal(SIGTERM, array(&$this, 'sigHandler')); ptl_signal(SIGUSR1, array(&$this, 'sigHandler')); ptl_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 = ptl_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 @aess 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 (ptl_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(); $mand = 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)) == $mand) { unlink($pCmdFile); unlink($pPwdFile); unlink($pHeartFile); // 删目录有缓存 usleep(1000); rmdir($pDir); } } // 新进程目录 if (!is_dir($processDir)) { mkdir($processDir, 0777); chmod($processDir, 0777); } // 写入命令参数 file_put_contents($processCmdFile, $mand); 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'; } }
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程