0

0

PHP 守护进程类

大家讲道理

大家讲道理

发布时间:2016-11-08 17:37:20

|

1565人浏览过

|

来源于php中文网

原创

用 php 实现的 daemon 类。可以在服务器上实现队列或者脱离 crontab 的计划任务。 

使用的时候,继承于这个类,并重写 _doTask 方法,通过 main 初始化执行。

<?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';
    }
}


LHT蓝海豚团购导航
LHT蓝海豚团购导航

Lht蓝海豚(大维)团购导航系统是一套开源程序,采用PHP+MySql平台开发,具有强大的多规则API采集功能、精确化引导消费功能,卓越的负载能力和访问速度,全面支持第三方整合(微博、短信等)前台功能团购名站:显示分类下的所有团购网站,点击团购站,可直接进行查看、推荐、收藏精品商城:为网民推荐展示非团购网站的网上商城今日团购:显示各团购网站正在进行的团购团购排行:今日团购商品可按价格、行业、折扣、

下载
PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

6

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

6

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

8

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

14

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

17

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

2

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

130

2026.02.27

deepseek在线提问
deepseek在线提问

本合集汇总了DeepSeek在线提问技巧与免登录使用入口,助你快速上手AI对话、写作、分析等功能。阅读专题下面的文章了解更多详细内容。

8

2026.02.27

AO3官网直接进入
AO3官网直接进入

AO3官网最新入口合集,汇总2026年可用官方及镜像链接,助你快速稳定访问Archive of Our Own平台。阅读专题下面的文章了解更多详细内容。

208

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 3.9万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 4万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号