php进程守护进程,分享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';

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值