侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

分享PHP守护进程类

php  /  管理员 发布于 7年前   156

用PHP实现的Daemon类。可以在服务器上实现队列或者脱离 crontab 的计划任务。 
使用的时候,继承于这个类,并重写 _doTask 方法,通过 main 初始化执行。

_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守护进程的两种常见实现方式详解
  • 如何写php守护进程(Daemon)
  • PHP守护进程实例
  • php守护进程 加linux命令nohup实现任务每秒执行一次
  • PHP守护进程化在C和PHP环境下的实现


  • 上一条:
    PHP使用strtotime获取上个月、下个月、本月的日期
    下一条:
    如何写php守护进程(Daemon)
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客