当前位置: 首页 > news >正文

PHP实现程序单例执行

原创文章,转载请注明出处: http://huyanping.sinaapp.com/?p=222
作者: Jenner

一、场景描写叙述:

近期我们一块业务。须要不断的监听一个文件夹的变化。假设文件夹中有文件,则启动PHP脚本处理掉。

最初的方案是使用crontab运行sh脚本,脚本大概例如以下:

SOK=`ps -ef |grep /www/sender.sh | grep -v grep|wc -l`
if [[ "$SOK" < "2" ]];then
        for f in `ls /www/queue`; do
                        php /www/logsender.php /www/queue/$f
        done


实际执行中出现了异常:ps -ef | grep xxx的方式,可能无法正确的推断进程是否正在执行。if条件永远都不会成立。使得PHP脚本永远不会执行。经过考虑后,决定建立一个独立于其它模块的,可以实现进程单例执行的类,解决问题。

 二、方案设计

1、通过PID文件实现进程单例

2、程序启动、退出自己主动创建、删除PID文件。做到不须要业务代码考虑PID文件删除

3、尽量保证代码独立性。不影响业务代码

三、原理

1、启动创建PID文件

2、绑定程序退出、被杀死等信号量,用于删除PID文件

3、加入析构函数。对象被销毁时,删除PID文件

四、遇到的问题

程序正常退出时。无法捕获到信号量,不知道是不是信号量选错了,ctrl+c等信号是正常的,假设能够解决捕获程序正常退出时的信号量。则能够替代析构函数方案,更加稳定

五、代码

<?php
/**
 * Created by PhpStorm.
 * User: huyanping
 * Date: 14-8-13
 * Time: 下午2:25
 *
 * 实现程序单例执行。调用方式:
 * declare(ticks = 1);//注意:一定要在外部调用文件里首部调用该声明,否则程序会无法监听到信号量
 * $single = new DaemonSingle(__FILE__);
 * $single->single();
 *
 */
 
class DaemonSingle {
 
    //PID文件路径
    private $pid_dir;
 
    //PID文件名
    private $filename;
 
    //PID文件完整路径名称
    private $pid_file;
 
    /**
     * 构造函数
     * @param $filename
     * @param string $pid_dir
     */
    public function __construct($filename, $pid_dir='/tmp/'){
        if(empty($filename)) throw new JetException('filename cannot be empty...');
        $this->filename = $filename;
        $this->pid_dir = $pid_dir;
        $this->pid_file = $this->pid_dir . DIRECTORY_SEPARATOR . substr(basename($this->filename), 0, -4) . '.pid';
    }
 
    /**
     * 单例模式启动接口
     * @throws JetException
     */
    public function single(){
        $this->check_pcntl();
        if(file_exists($this->pid_file)) {
            throw new Exception('the process is already running...');
        }
        $this->create_pid_file();
    }
 
    /**
     * @throws JetException
     */
    private function create_pid_file()
    {
        if (!is_dir($this->pid_dir)) {
            mkdir($this->pid_dir);
        }
        $fp = fopen($this->pid_file, 'w');
        if(!$fp){
            throw new Exception('cannot create pid file...');
        }
        fwrite($fp, posix_getpid());
        fclose($fp);
        $this->pid_create = true;
    }
 
    /**
     * 环境检查
     * @throws Exception
     */
    public function check_pcntl()
    {
        // Make sure PHP has support for pcntl
        if (!function_exists('pcntl_signal')) {
            $message = 'PHP does not appear to be compiled with the PCNTL extension.  This is neccesary for daemonization';
            throw new Exception($message);
        }
        //信号处理
        pcntl_signal(SIGTERM, array(&$this, "signal_handler"));
        pcntl_signal(SIGINT, array(&$this, "signal_handler"));
        pcntl_signal(SIGQUIT, array(&$this, "signal_handler"));
 
        // Enable PHP 5.3 garbage collection
        if (function_exists('gc_enable')) {
            gc_enable();
            $this->gc_enabled = gc_enabled();
        }
    }
 
    /**
     * 信号处理函数,程序异常退出时。安全删除PID文件
     * @param $signal
     */
    public function signal_handler($signal)
    {
        switch ($signal) {
            case SIGINT :
            case SIGQUIT:
            case SIGTERM:{
                self::safe_quit();
                break;
            }
        }
    }
 
    /**
     * 安全退出,删除PID文件
     */
    public function safe_quit()
    {
        if (file_exists($this->pid_file)) {
            $pid = intval(posix_getpid());
            $file_pid = intval(file_get_contents($this->pid_file));
            if($pid == $file_pid){
                unlink($this->pid_file);
            }
        }
        posix_kill(0, SIGKILL);
        exit(0);
    }
 
    /**
     * 析构函数,删除PID文件
     */
    public function __destruct(){
            $this->safe_quit();
    }
}



相关文章:

  • Visual C#.Net网络程序开发-Tcp篇(3)
  • [New Portal]Windows Azure Virtual Machine (3) 在VM上挂载磁盘
  • ajax.net 我曾经轻视了他,郁闷。
  • 彻底去除Win10“快速访问”
  • 网络服务器时间发布地址
  • 《Python地理数据处理》——2.3 变量
  • 只能使用数组初始值设定项表达式为数组类型赋值。请尝试改用新的表达式
  • 《Total Commander:万能文件管理器》——12.3.高人高见
  • 在C#中应用哈希表(Hashtable)
  • 《Python数据可视化编程实战》——5.2 创建3D柱状图
  • c#.net中参数修饰符ref,out ,params的区别
  • 《JavaScript设计模式》——9.10 Factory(工厂)模式
  • 以太网帧格式、IP数据报格式、TCP段格式+UDP段格式 详解
  • webgis实现技术分析
  • 《Oracle数据库性能优化方法论和最佳实践》——第3章 流程分析之数据库登录流程 3.1 数据库登录导致业务系统性能恶化案例分享...
  • Angular2开发踩坑系列-生产环境编译
  • Angular4 模板式表单用法以及验证
  • Mocha测试初探
  • nodejs:开发并发布一个nodejs包
  • Shell编程
  • SpringCloud集成分布式事务LCN (一)
  • Spring核心 Bean的高级装配
  • uva 10370 Above Average
  • Vue组件定义
  • 阿里云应用高可用服务公测发布
  • 构造函数(constructor)与原型链(prototype)关系
  • 记录一下第一次使用npm
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 前端学习笔记之观察者模式
  • 入门级的git使用指北
  • 树莓派 - 使用须知
  • 微信支付JSAPI,实测!终极方案
  • 我是如何设计 Upload 上传组件的
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 【云吞铺子】性能抖动剖析(二)
  • gunicorn工作原理
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​如何防止网络攻击?
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • # 计算机视觉入门
  • (done) 两个矩阵 “相似” 是什么意思?
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (南京观海微电子)——COF介绍
  • (三)终结任务
  • (四)c52学习之旅-流水LED灯
  • (图)IntelliTrace Tools 跟踪云端程序
  • (一)基于IDEA的JAVA基础10
  • (转)甲方乙方——赵民谈找工作
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET 8.0 中有哪些新的变化?
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET Micro Framework 4.2 beta 源码探析