分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 软件开发

php 守护进程类

发布时间:2023-09-06 01:19责任编辑:彭小芳关键词:暂无标签

  最近个人项目中需要后台运行任务,之前一直是用nouhp & + 重定向输出 来后台跑任务,后来觉得不好维护原始数据,同时可能也没有直接操作进程那么稳吧(没验证)。废话少说,来看分析。

首先,我们守护进程的主要目的是创建一个长生存期的进程,独立于控制端去完成你设置的任务,与此同时可以加入事件监听或者 状态轮询,构成一个完整的运行机制


  php中使用pcntl_fork()来 创建子进程,如下程序会在父进程和子进程中分别运行,区别是返回的pid不一样:

$pid = pcntl_fork();if ($pid == -1) { 创建失败 ???// 当 pid 为 -1 的时候表示创建子进程失败,这时返回-1。 ???return false;} else if ($pid) {//这里运行的是父进程,但是pid 指的是他的子进程pid ???} else { //pid 为0 的时候,说明运行的子进程}

    以上你需要 想象着 在不同的通道里去执行代码,进程只是计算机操作系统初期提出来一个形象化的概念,包括后面更加粒度更细化的线程。

  需要注意的是 ,你想要独立出来完成任务,那么就不再依赖原来的环境了,包括目录,上下文环境,

     这个时候我需要 php提供了 一些 set方法,用来设置进程的上下文环境,如下:

  posix_setsid(); chdir("/")
  可参考http://php.net/manual/zh/function.posix-setsid.php

  接着 为了让进程便于我们控制并稳定的运行,我利用了 linux系统里面的信号,通过信号来处理 相应的操作,
对于php提供的安装信号的操作函数,可以使用 ?pcntl_signal(),http://php.net/manual/zh/function.pcntl-signal.php
对于信号的参考可以查看一下文章:http://www.cnblogs.com/guixiaoming/p/7700132.html
使用的例子(强行停止):
posix_kill($pid, SIGKILL)


最后就是一些异常处理,以下是我的个人项目中使用的守护进程:
abstract class DaemonBase{ ???const LOG_ECHO = 1; ???const LOG_FILE = 2; ???/** ????* @var $child_pid 子进程的pid ????*/ ???public $child_pid; ???public $pid_file; ???public $log_file; //每个子类的日志文件 ???public $switch = true; //开关 ???????public $cnt; //重启的计数 ???public $do_works = []; ????public $sig_handlers = []; ???/** ????* 配置 ????*/ ???public static $config = [ ???????‘current_dir‘ ?=> ‘‘, ???????‘log_file‘ ????=> ‘/tmp/‘ . __CLASS__ . ‘.log‘, ???????‘pid‘ ?????????=> ‘/tmp/‘ . ‘daemon‘ . ‘.pid‘, ???????‘limit_memory‘ => -1, //分配最大内存 ???????‘max_times‘ ???=> 0, //重启的最大次数 ???]; ???/** ????* 构造函数,设置path.以及注册shutdown ????*/ ???public function __construct() ???{ ???????$this->pid_file = ‘/tmp/‘ . get_class($this) . ‘.pid‘; ???????$this->log_file = ‘/tmp/‘ . get_class($this) . ‘.log‘; ???????set_error_handler([$this, ‘errorHandler‘]); ???????register_shutdown_function(array($this, ‘shutdown‘)); ???????ini_set(‘memory_limit‘, self::$config[‘limit_memory‘]); ???????ini_set(‘display_errors‘, ‘Off‘); ???????clearstatcache(); ???} ???/** ????* 执行启动程序 ????* @param string $command ????*/ ???public function run($command = ‘start‘) ???{ ???????if(empty($command) || !in_array($command, [‘start‘, ‘stop‘, ‘restart‘, ‘status‘])){ ???????????$command = ‘help‘; ???????} ???????$this->$command(); ???} ???/** ????* 开始 ????*/ ???public function start() ???{ ???????$this->log(‘Starting daemon...‘, self::LOG_ECHO | self::LOG_FILE); ???????$this->daemonize(); ??????echo ‘Daemon #‘ . $this->getChildPid() . ‘ 启动成功‘ .PHP_EOL; ???????declare(ticks = 1){ ???????????while($this->switch){ ???????????????$this->autoRestart(); ???????????????$this->todo(); ???????????????try { ???????????????????????????????????????$this->main(); ???????????????}catch (Exception $e) { ???????????????????var_dump($e); ???????????????????$this->log($e->getMessage(), self::LOG_FILE); ???????????????} ???????????} ???????} ???} ???/** ????* 停止 ????*/ ???public function stop() ???{ ???????if (!$pid = $this->getChildPid()) { ???????????$this->log(‘守护进程 GG‘, self::LOG_FILE); ???????????exit(); ???????} ???????posix_kill($pid, SIGKILL); ???} ???protected function stopAll() ???{ ???????if (!is_writeable($this->log_file)) { ???????????$this->log(‘Daemon (no pid file) not running‘, self::LOG_ECHO); ???????????return FALSE; ???????} ???????$pid = $this->getChildPid(); ???????unlink($this->log_file); ???????$this->log(‘Daemon #‘ . $pid . ‘ has stopped‘, self::LOG_ECHO | self::LOG_FILE); ???????$this->switch = TRUE; ???} ???public function restart() ???{ ???????if (!$pid = $this->getChildPid()) { ???????????$this->log(‘守护进程 GG‘, self::LOG_FILE); ???????????exit(); ???????} ???????posix_kill($pid, SIGHUP); ???} ???public function status() ???{ ???????if($pid = $this->getChildPid()) { ???????????$msg = "pid: $pid is running"; ???????}else { ???????????$msg = "进程GG"; ???????} ???????$this->log($msg, self::LOG_ECHO); ???} ???/** ????* 帮助命令 ????*/ ???public function help() ???{ ???????echo ‘start | stop | status | restart‘; ???} ???/** ????* 检测能否正常启动 ????* @return bool ????*/ ???protected function check() ???{ ???????if ($pid = $this->getChildPid()) { ???????????$this->log("Daemon #{$pid} has already started", self::LOG_ECHO); ???????????return FALSE; ???????} ???????$dir = dirname(self::$config[‘pid‘]); ???????if (!is_writable($dir)) { ???????????$this->log("you do not have permission to write pid file @ {$dir}", self::LOG_ECHO); ???????????return FALSE; ???????} ???????if (!is_writable(self::$config[‘log_file‘]) || !is_writable(dirname(self::$config[‘log_file‘]))) { ???????????$this->log("you do not have permission to write log file: {log_file}", self::LOG_ECHO); ???????????return FALSE; ???????} ???????if (!defined(‘SIGHUP‘)) { // Check for pcntl ???????????$this->log(‘PHP is compiled without --enable-pcntl directive‘, self::LOG_ECHO | self::LOG_FILE); ???????????return FALSE; ???????} ???????if (‘cli‘ !== php_sapi_name()) { // Check for CLI ???????????$this->log(‘You can only create daemon from the command line (CLI-mode)‘, self::LOG_ECHO | self::LOG_FILE); ???????????return FALSE; ???????} ???????if (!function_exists(‘posix_getpid‘)) { // Check for POSIX ???????????$this->log(‘PHP is compiled without --enable-posix directive‘, self::LOG_ECHO | self::LOG_FILE); ???????????return FALSE; ???????} ???????return TRUE; ???} ???/** ????* 创建子进程,并做好信号处理工作 ????*/ ???protected function daemonize() ???{ ???????//检查状态 ???????$this->check(); ???????//fork 子进程 ???????$this->fork(); ???????//信号处理 ???????$sig_array = [ ???????????SIGTERM => [$this, ‘defaultSigHandler‘], ???????????SIGQUIT => [$this, ‘defaultSigHandler‘], ???????????SIGINT ?=> [$this, ‘defaultSigHandler‘], ???????????SIGHUP ?=> [$this, ‘defaultSigHandler‘], ???????]; ???????foreach ($sig_array as $signo => $callback) { ???????????pcntl_signal($signo, $callback); ???????} ??????file_put_contents($this->pid_file, $this->child_pid); ???} ???/** ????* fork 子进程 ????* @return bool ????*/ ???protected function fork() ???{ ???????$pid = pcntl_fork(); ???????if($pid == -1) { //创建子进程失败 ???????????return false; ???????} ???????if($pid) { // 父进程 ???????????exit(); ???????} ???????//子进程 ???????$this->child_pid = posix_getpid(); //子进程id ???????posix_setsid(); //使进程成为会话组长,让进程摆脱原会话的控制;让进程摆脱原进程组的控制; ???????return true; ???} ???/** ????* 重启 ????*/ ???protected function autoRestart() ???{ ???????if((self::$config[‘max_times‘] && $this->cnt >= self::$config[‘max_time‘]) || ???????????(0 !== self::$config[‘limit_memory‘] && memory_get_usage(TRUE) >= self::$config[‘limit_memory‘])) ???????{ ???????????$this->doworks = [[$this, ‘restart‘]]; ???????????$this->cnt = 0; ???????} ???????$this->cnt++; ???} ???public function getChildPid(){ ???????if(!file_exists($this->pid_file)){ ???????????return false; ???????} ???????$pid = (int)file_get_contents($this->pid_file); ???????return file_exists("/proc/{$pid}") ? $pid : FALSE; //检测是否确实存在此进程 ???} ???public function todo() ???{ ???????foreach ($this->do_works as $row) { ???????????(1 === count($row)) ? call_user_func($row[0]) : call_user_func_array($row[0], $row[1]); ???????} ???} ???/** ????* 需要执行的逻辑体 ????* ????* @return mixed ????*/ ???abstract public function main(); ???public function defaultSigHandler($signo) ???{ ???????switch ($signo) { ???????????case SIGTERM: ???????????case SIGQUIT: ???????????case SIGINT: ???????????????$this->do_works = [[$this, ‘stop‘]]; ???????????????break; ???????????case SIGHUP: ???????????????$this->do_works = [[$this, ‘restart‘]]; ???????????????break; ???????????default: ???????????????break; ???????} ???} ???/** ????* Regist signo handler ????* ????* @param int $sig ????* @param callback $action ????*/ ???public function regSigHandler($sig, $action) ???{ ???????$this->sig_handlers[$sig] = $action; ???} ???public function errorHandler($error_code, $msg){ ???} ???/** ????* 守护进程日志 ????* ????* @param string $msg ????* @param int $io, 1->just echo, 2->just write, 3->echo & write ????*/ ???public function log($msg, $io = self::LOG_FILE) ???{ ???????$datetime = date(‘Y-m-d H:i:s‘); ???????$msg = "[{$datetime}] {$msg}\n"; ???????if ((self::LOG_ECHO & $io) && !$this->child_pid) { ???????????echo $msg, "\n"; ???????} ???????if (self::LOG_FILE & $io) { ???????????file_put_contents($this->log_file, $msg, FILE_APPEND | LOCK_EX); ???????} ???} ???/** ????* 脚本跑完执行 ????*/ ???public function shutdown() ???{ ???????if ($error = error_get_last()) { ???????????$this->log(implode(‘|‘, $error), self::LOG_FILE); ???????} ???????if (is_writeable(self::$config[‘pid‘]) && $this->child_pid) { ???????????unlink(self::$config[‘pid‘]); ???????} ???}

php 守护进程类

原文地址:http://www.cnblogs.com/guixiaoming/p/7716709.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved