当前位置:首页 > PHP教程 > php高级应用 > 列表

分享PHP守护进程类

发布:smiling 来源: PHP粉丝网  添加日期:2021-07-03 20:05:49 浏览: 评论:0 

这篇文章主要介绍了PHP守护进程类,守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,守护进程是一种很有用的进程。php也可以实现守护进程的功能,需要的朋友可以参考下

用PHP实现的Daemon类。可以在服务器上实现队列或者脱离 crontab 的计划任务。

使用的时候,继承于这个类,并重写 _doTask 方法,通过 main 初始化执行。

  1. <?php 
  2.    
  3. class Daemon { 
  4.    
  5.   const DLOG_TO_CONSOLE = 1; 
  6.   const DLOG_NOTICE = 2; 
  7.   const DLOG_WARNING = 4; 
  8.   const DLOG_ERROR = 8; 
  9.   const DLOG_CRITICAL = 16; 
  10.    
  11.   const DAPC_PATH = '/tmp/daemon_apc_keys'
  12.    
  13.   /** 
  14.    * User ID 
  15.    * 
  16.    * @var int 
  17.    */ 
  18.   public $userID = 65534; // nobody 
  19.    
  20.   /** 
  21.    * Group ID 
  22.    * 
  23.    * @var integer 
  24.    */ 
  25.   public $groupID = 65533; // nobody 
  26.    
  27.   /** 
  28.    * Terminate daemon when set identity failure ? 
  29.    * 
  30.    * @var bool 
  31.    * @since 1.0.3 
  32.    */ 
  33.   public $requireSetIdentity = false; 
  34.    
  35.   /** 
  36.    * Path to PID file 
  37.    * 
  38.    * @var string 
  39.    * @since 1.0.1 
  40.    */ 
  41.   public $pidFileLocation = '/tmp/daemon.pid'
  42.    
  43.   /** 
  44.    * processLocation 
  45.    * 进程信息记录目录 
  46.    * 
  47.    * @var string 
  48.    */ 
  49.   public $processLocation = ''
  50.    
  51.   /** 
  52.    * processHeartLocation 
  53.    * 进程心跳包文件 
  54.    * 
  55.    * @var string 
  56.    */ 
  57.   public $processHeartLocation = ''
  58.    
  59.   /** 
  60.    * Home path 
  61.    * 
  62.    * @var string 
  63.    * @since 1.0 
  64.    */ 
  65.   public $homePath = '/'
  66.    
  67.   /** 
  68.    * Current process ID 
  69.    * 
  70.    * @var int 
  71.    * @since 1.0 
  72.    */ 
  73.   protected $_pid = 0; 
  74.    
  75.   /** 
  76.    * Is this process a children 
  77.    * 
  78.    * @var boolean 
  79.    * @since 1.0 
  80.    */ 
  81.   protected $_isChildren = false; 
  82.    
  83.   /** 
  84.    * Is daemon running 
  85.    * 
  86.    * @var boolean 
  87.    * @since 1.0 
  88.    */ 
  89.   protected $_isRunning = false; 
  90.    
  91.   /** 
  92.    * Constructor 
  93.    * 
  94.    * @return void 
  95.    */ 
  96.   public function __construct() { 
  97.    
  98.     error_reporting(0); 
  99.     set_time_limit(0); 
  100.     ob_implicit_flush(); 
  101.    
  102.     register_shutdown_function(array(&$this'releaseDaemon')); 
  103.   } 
  104.    
  105.   /** 
  106.    * 启动进程 
  107.    * 
  108.    * @return bool 
  109.    */ 
  110.   public function main() { 
  111.    
  112.     $this->_logMessage('Starting daemon'); 
  113.    
  114.     if (!$this->_daemonize()) { 
  115.       $this->_logMessage('Could not start daemon', self::DLOG_ERROR); 
  116.    
  117.       return false; 
  118.     } 
  119.    
  120.     $this->_logMessage('Running...'); 
  121.    
  122.     $this->_isRunning = true; 
  123.    
  124.     while ($this->_isRunning) { 
  125.       $this->_doTask(); 
  126.     } 
  127.    
  128.     return true; 
  129.   } 
  130.    
  131.   /** 
  132.    * 停止进程 
  133.    * 
  134.    * @return void 
  135.    */ 
  136.   public function stop() { 
  137.    
  138.     $this->_logMessage('Stoping daemon'); 
  139.    
  140.     $this->_isRunning = false; 
  141.   } 
  142.    
  143.   /** 
  144.    * Do task 
  145.    * 
  146.    * @return void 
  147.    */ 
  148.   protected function _doTask() { 
  149.     // override this method 
  150.   } 
  151.    
  152.   /** 
  153.    * _logMessage 
  154.    * 记录日志 
  155.    * 
  156.    * @param string 消息 
  157.    * @param integer 级别 
  158.    * @return void 
  159.    */ 
  160.   protected function _logMessage($msg$level = self::DLOG_NOTICE) { 
  161.     // override this method 
  162.   } 
  163.    
  164.   /** 
  165.    * Daemonize 
  166.    * 
  167.    * Several rules or characteristics that most daemons possess: 
  168.    * 1) Check is daemon already running 
  169.    * 2) Fork child process 
  170.    * 3) Sets identity 
  171.    * 4) Make current process a session laeder 
  172.    * 5) Write process ID to file 
  173.    * 6) Change home path 
  174.    * 7) umask(0) 
  175.    * 
  176.    * @access private 
  177.    * @since 1.0 
  178.    * @return void 
  179.    */ 
  180.   private function _daemonize() { 
  181.    
  182.     ob_end_flush(); 
  183.    
  184.     if ($this->_isDaemonRunning()) { 
  185.       // Deamon is already running. Exiting 
  186.       return false; 
  187.     } 
  188.    
  189.     if (!$this->_fork()) { 
  190.       // Coudn't fork. Exiting. 
  191.       return false; 
  192.     } 
  193.    
  194.     if (!$this->_setIdentity() && $this->requireSetIdentity) { 
  195.       // Required identity set failed. Exiting 
  196.       return false; 
  197.     } 
  198.    
  199.     if (!posix_setsid()) { 
  200.       $this->_logMessage('Could not make the current process a session leader', self::DLOG_ERROR); 
  201.    
  202.       return false; 
  203.     } 
  204.    
  205.     if (!$fp = fopen($this->pidFileLocation, 'w')) { 
  206.       $this->_logMessage('Could not write to PID file', self::DLOG_ERROR); 
  207.       return false; 
  208.     } else { 
  209.       fputs($fp$this->_pid); 
  210.       fclose($fp); 
  211.     } 
  212.    
  213.     // 写入监控日志 
  214.     $this->writeProcess(); 
  215.    
  216.     chdir($this->homePath); 
  217.     umask(0); 
  218.    
  219.     declare(ticks = 1); 
  220.    
  221.     pcntl_signal(SIGCHLD, array(&$this'sigHandler')); 
  222.     pcntl_signal(SIGTERM, array(&$this'sigHandler')); 
  223.     pcntl_signal(SIGUSR1, array(&$this'sigHandler')); 
  224.     pcntl_signal(SIGUSR2, array(&$this'sigHandler')); 
  225.    
  226.     return true; 
  227.   } 
  228.    
  229.   /** 
  230.    * Cheks is daemon already running 
  231.    * 
  232.    * @return bool 
  233.    */ 
  234.   private function _isDaemonRunning() { 
  235.    
  236.     $oldPid = file_get_contents($this->pidFileLocation); 
  237.    
  238.     if ($oldPid !== false && posix_kill(trim($oldPid),0)) 
  239.     { 
  240.       $this->_logMessage('Daemon already running with PID: '.$oldPid, (self::DLOG_TO_CONSOLE | self::DLOG_ERROR)); 
  241.    
  242.       return true; 
  243.     } 
  244.     else 
  245.     { 
  246.       return false; 
  247.     } 
  248.   } 
  249.    
  250.   /** 
  251.    * Forks process 
  252.    * 
  253.    * @return bool 
  254.    */ 
  255.   private function _fork() { 
  256.    
  257.     $this->_logMessage('Forking...'); 
  258.    
  259.     $pid = pcntl_fork(); 
  260.    
  261.     if ($pid == -1) { 
  262.       // 出错 
  263.       $this->_logMessage('Could not fork', self::DLOG_ERROR); 
  264.    
  265.       return false; 
  266.     } elseif ($pid) { 
  267.       // 父进程 
  268.       $this->_logMessage('Killing parent'); 
  269.    
  270.       exit(); 
  271.     } else { 
  272.       // fork的子进程 
  273.       $this->_isChildren = true; 
  274.       $this->_pid = posix_getpid(); 
  275.    
  276.       return true; 
  277.     } 
  278.   } 
  279.    
  280.   /** 
  281.    * Sets identity of a daemon and returns result 
  282.    * 
  283.    * @return bool 
  284.    */ 
  285.   private function _setIdentity() { 
  286.    
  287.     if (!posix_setgid($this->groupID) || !posix_setuid($this->userID)) 
  288.     { 
  289.       $this->_logMessage('Could not set identity', self::DLOG_WARNING); 
  290.    
  291.       return false; 
  292.     } 
  293.     else 
  294.     { 
  295.       return true; 
  296.     } 
  297.   } 
  298.    
  299.   /** 
  300.    * Signals handler 
  301.    * 
  302.    * @access public 
  303.    * @since 1.0 
  304.    * @return void 
  305.    */ 
  306.   public function sigHandler($sigNo) { 
  307.    
  308.     switch ($sigNo
  309.     { 
  310.       case SIGTERM:  // Shutdown 
  311.         $this->_logMessage('Shutdown signal'); 
  312.         exit(); 
  313.         break
  314.    
  315.       case SIGCHLD:  // Halt 
  316.         $this->_logMessage('Halt signal'); 
  317.         while (pcntl_waitpid(-1, $status, WNOHANG) > 0); 
  318.         break
  319.       case SIGUSR1:  // User-defined 
  320.         $this->_logMessage('User-defined signal 1'); 
  321.         $this->_sigHandlerUser1(); 
  322.         break
  323.       case SIGUSR2:  // User-defined 
  324.         $this->_logMessage('User-defined signal 2'); 
  325.         $this->_sigHandlerUser2(); 
  326.         break
  327.     } 
  328.   } 
  329.    
  330.   /** 
  331.    * Signals handler: USR1 
  332.    * 主要用于定时清理每个进程里被缓存的域名dns解析记录 
  333.    * 
  334.    * @return void 
  335.    */ 
  336.   protected function _sigHandlerUser1() { 
  337.     apc_clear_cache('user'); 
  338.   } 
  339.    
  340.   /** 
  341.    * Signals handler: USR2 
  342.    * 用于写入心跳包文件 
  343.    * 
  344.    * @return void 
  345.    */ 
  346.   protected function _sigHandlerUser2() { 
  347.    
  348.     $this->_initProcessLocation(); 
  349.    
  350.     file_put_contents($this->processHeartLocation, time()); 
  351.    
  352.     return true; 
  353.   } 
  354.    
  355.   /** 
  356.    * Releases daemon pid file 
  357.    * This method is called on exit (destructor like) 
  358.    * 
  359.    * @return void 
  360.    */ 
  361.   public function releaseDaemon() { 
  362.    
  363.     if ($this->_isChildren && is_file($this->pidFileLocation)) { 
  364.       $this->_logMessage('Releasing daemon'); 
  365.    
  366.       unlink($this->pidFileLocation); 
  367.     } 
  368.   } 
  369.    
  370.   /** 
  371.    * writeProcess 
  372.    * 将当前进程信息写入监控日志,另外的脚本会扫描监控日志的数据发送信号,如果没有响应则重启进程 
  373.    * 
  374.    * @return void 
  375.    */ 
  376.   public function writeProcess() { 
  377.    
  378.     // 初始化 proc 
  379.     $this->_initProcessLocation(); 
  380.    
  381.     $command = trim(implode(' '$_SERVER['argv'])); 
  382.    
  383.     // 指定进程的目录 
  384.     $processDir = $this->processLocation . '/' . $this->_pid; 
  385.     $processCmdFile = $processDir . '/cmd'
  386.     $processPwdFile = $processDir . '/pwd'
  387.    
  388.     // 所有进程所在的目录 
  389.     if (!is_dir($this->processLocation)) { 
  390.       mkdir($this->processLocation, 0777); 
  391.       chmod($processDir, 0777); 
  392.     } 
  393.    
  394.     // 查询重复的进程记录 
  395.     $pDirObject = dir($this->processLocation); 
  396.     while ($pDirObject && (($pid = $pDirObject->read()) !== false)) { 
  397.       if ($pid == '.' || $pid == '..' || intval($pid) != $pid) { 
  398.         continue
  399.       } 
  400.    
  401.       $pDir = $this->processLocation . '/' . $pid
  402.       $pCmdFile = $pDir . '/cmd'
  403.       $pPwdFile = $pDir . '/pwd'
  404.       $pHeartFile = $pDir . '/heart'
  405.    
  406.       // 根据cmd检查启动相同参数的进程 
  407.       if (is_file($pCmdFile) && trim(file_get_contents($pCmdFile)) == $command) { 
  408.         unlink($pCmdFile); 
  409.         unlink($pPwdFile); 
  410.         unlink($pHeartFile); 
  411.    
  412.         // 删目录有缓存 
  413.         usleep(1000); 
  414.    
  415.         rmdir($pDir); 
  416.       } 
  417.     } 
  418.    
  419.     // 新进程目录 
  420.     if (!is_dir($processDir)) { 
  421.       mkdir($processDir, 0777); 
  422.       chmod($processDir, 0777); 
  423.     } 
  424.    
  425.     // 写入命令参数 
  426.     file_put_contents($processCmdFile$command); 
  427.     file_put_contents($processPwdFile$_SERVER['PWD']); 
  428.    
  429.     // 写文件有缓存 
  430.     usleep(1000); 
  431.    
  432.     return true; 
  433.   } 
  434.    
  435.   /** 
  436.    * _initProcessLocation 
  437.    * 初始化 
  438.    * 
  439.    * @return void 
  440.    */ 
  441.   protected function _initProcessLocation() { 
  442.    
  443.     $this->processLocation = ROOT_PATH . '/app/data/proc'
  444.     $this->processHeartLocation = $this->processLocation . '/' . $this->_pid . '/heart'
  445.   } 
  446. }

Tags: PHP守护进程类

分享到: