热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分享PHP守护进程类

:本篇文章主要介绍了分享PHP守护进程类,对于PHP教程有兴趣的同学可以参考一下。
用PHP实现的Daemon类。可以在服务器上实现队列或者脱离 crontab 的计划任务。
使用的时候,继承于这个类,并重写 _doTask 方法,通过 main 初始化执行。

<&#63;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 &#63;
   *
   * @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守护进程类,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

推荐阅读
  • 本文详细介绍了如何在ARM架构的目标设备上部署SSH服务端,包括必要的软件包下载、交叉编译过程以及最终的服务配置与测试。适合嵌入式开发人员和系统集成工程师参考。 ... [详细]
  • 随着Linux操作系统的广泛使用,确保用户账户及系统安全变得尤为重要。用户密码的复杂性直接关系到系统的整体安全性。本文将详细介绍如何在CentOS服务器上自定义密码规则,以增强系统的安全性。 ... [详细]
  • 本文详细介绍如何安装和配置DedeCMS的移动端站点,包括新版本安装、老版本升级、模板适配以及必要的代码修改,以确保移动站点的正常运行。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 本文介绍了如何利用jQuery实现对网页上多个div元素的显示与隐藏控制,包括基本的toggle方法及更复杂的显示隐藏逻辑。 ... [详细]
  • Node.js在服务器上的多种部署策略
    本文探讨了Node.js应用程序在服务器上部署的几种有效方法,包括使用Screen、PM2以及通过宝塔面板进行简易管理。 ... [详细]
  • 本文详细介绍了如何在Oracle VM VirtualBox中实现主机与虚拟机之间的数据交换,包括安装Guest Additions增强功能,以及如何利用这些功能进行文件传输、屏幕调整等操作。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • 调试利器SSH隧道
    在开发微信公众号或小程序的时候,由于微信平台规则的限制,部分接口需要通过线上域名才能正常访问。但我们一般都会在本地开发,因为这能快速的看到 ... [详细]
  • 解决PHP项目在服务器无法抓取远程网页内容的问题
    本文探讨了在使用PHP进行后端开发时,遇到的一个常见问题:即在本地环境中能够正常通过CURL获取远程网页内容,但在服务器上却无法实现。我们将分析可能的原因并提供解决方案。 ... [详细]
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • 在 Ubuntu 22.04 LTS 上部署 Jira 敏捷项目管理工具
    Jira 敏捷项目管理工具专为软件开发团队设计,旨在以高效、有序的方式管理项目、问题和任务。该工具提供了灵活且可定制的工作流程,能够根据项目需求进行调整。本文将详细介绍如何在 Ubuntu 22.04 LTS 上安装和配置 Jira。 ... [详细]
author-avatar
陈应锋forever
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有