php 守护进程类
最近个人项目中需要后台运行任务,之前一直是用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 守护进程类的更多相关文章
- python中的daemon守护进程实现方法
原文参考:http://blog.csdn.net/tao_627/article/details/49532021 守护进程是生存期长的一种进程.它们独立于控制终端并且周期性的执行某种任务或等待处理 ...
- RabbitMQ PHP操作类,守护进程及相关测试数据
封装类如下: <?php /* * amqp协议操作类,可以访问rabbitMQ * 需先安装php_amqp扩展 */ class RabbitMQCommand{ public $confi ...
- 并发编程(二)--利用Process类开启进程、僵尸进程、孤儿进程、守护进程、互斥锁、队列与管道
一.multiprocessing模块 1.multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似. 2.mu ...
- 并发编程(二)——利用Process类开启进程、僵尸进程、孤儿进程、守护进程、互斥锁、队列与管道
Process类与开启进程.守护进程.互斥锁 一.multiprocessing模块 1.multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模 ...
- Linux Supervisor 守护进程基本配置
supervisor:C/S架构的进程控制系统,可使用户在类UNIX系统中监控.管理进程.常用于管理与某个用户或项目相关的进程. 组成部分supervisord:服务守护进程supervisorctl ...
- Android 通过JNI实现守护进程,使得Service服务不被杀死
来自: http://finalshares.com/read-7306 转载请注明出处: http://blog.csdn.net/yyh352091626/article/details/5054 ...
- python实现Linux启动守护进程
python实现Linux启动守护进程 DaemonClass.py代码: #/usr/bin/env python # -*- coding: utf-8 -*- import sys import ...
- centos 下Supervisor 守护进程基本配置
supervisor:C/S架构的进程控制系统,可使用户在类UNIX系统中监控.管理进程.常用于管理与某个用户或项目相关的进程. 组成部分supervisord:服务守护进程supervisorctl ...
- php写守护进程(Daemon)
守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程是一种很有用的进程.php也可以实现守护进程的功能. 1.基本概念 进程 ...
随机推荐
- 201521123074 《Java程序设计》第4周学习总结
1.本周学习总结 这次用XMind画的图,果然比百度脑图好用一点. 2.书面作业 Q1.注释的应用 使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图) 类注释 方 ...
- 201521123037 《Java程序设计》第4周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结上课内容. 1.识别类.对于一个系统中,对于名词大多为类或属性,对于动词大多为方法. 2.注释.类注释.方法 ...
- Java 课程设计 "Give it up"小游戏(团队)
JAVA课程设计 "永不言弃"小游戏(From :Niverse) 通过Swing技术创建游戏的登陆注册界面,使用mySQL数据库技术完成用户的各项信息保存和游戏完成后的成绩保存. ...
- Java课程设计----仿Windows标准型计算器
JAVA课程设计 仿Windows标准型计算器(By Yanboooooooo) 一.团队介绍: 连燕波[组长]:网络1513学生. 张文博[组员]:网络1513学生. 二.项目git地址 码云项目地 ...
- lintcode.177 把排序数组转换为高度最小的二叉搜索树
把排序数组转换为高度最小的二叉搜索树 描述 笔记 数据 评测 给一个排序数组(从小到大),将其转换为一棵高度最小的排序二叉树. 注意事项 There may exist multiple val ...
- Spring配置属性文件
在项目开发阶段和交付阶段数据库的连接信息往往是不同的,可以把这些信息写成属性文件,再在Spring中导入即可引用 jdbc.properties属性文件如下: jdbc.driverClassName ...
- 作为一个新人,怎样学习嵌入式Linux
作为一个新人,怎样学习嵌入式Linux?被问过太多次,特写这篇文章来回答一下. 在学习嵌入式Linux之前,肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会). C语言要学 ...
- 性能压测诡异的Requests/second 响应刺尖问题
最近一段时间都在忙着转java项目最后的冲刺,前期的coding翻代码.debug.fixbug都逐渐收尾,进入上线前的性能压测. 虽然不是大促前的性能压测要求,但是为了安全起见,需要摸个底心里有个数 ...
- oracle pl/sql 分页
一.无返回值的存储过程 古人云:欲速则不达,为了让大家伙比较容易接受分页过程编写,我还是从简单到复杂,循序渐进的给大家讲解.首先是掌握最简单的存储过程,无返回值的存储过程. 案例:现有一张表book, ...
- ThinkPHP中:检查Session是否过期
1.创建Session public function index(){ $sess_time=time(); session('name','andy'); session('time_stamp' ...