原创文章,转载请注明出处: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();
}
}

PHP实现程序单例执行的更多相关文章

  1. C#实现程序单例日志输出

    对于一个完整的程序系统,一个日志记录是必不可少的.可以用它来记录程序在运行过程中的运行状态和报错信息.比如,那些不想通过弹框提示的错误,程序执行过程中捕获的异常等. 首先,在你的解决方案中,适当的目录 ...

  2. C#应用程序单例并激活程序的窗口 使其显示在最前端

    public class SoftHelper { ///<summary> /// 该函数设置由不同线程产生的窗口的显示状态 /// </summary> /// <p ...

  3. Inno Setup安装程序单例运行

    1.源起: KV项目下载底层升级包,老是报出升级文件占用问题,反复分析,不得其所. 今天突然发现同时启动多个升级程序实例,分析认为安装包同时被调用多次,引发实例访问文件冲突,导致此问题. 安装程序由I ...

  4. C# 应用程序单例(禁止多开) 获取.net版本号 以及 管理员权限

    Mutex不仅提供跨线程的服务,还提供跨进程的服务.当在构造函数中为Mutex指定名称时,则会创建一个命名了的Mutex.其他线程创建Mutex时,如果指定的名称相同,则返回同一个互斥体,不论该线程位 ...

  5. 编写一个Singleton程序(单例)

    public class Test { private static Test test = new Test(); private Test(){}//构造方法私有化 private static ...

  6. 23种设计模式之单例(Singleton Pattern)

    单例 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例(eg:应对一些特殊情况,比如数据库连接池(内置了资源)  全局唯一号码生成器),才能确保它们的逻辑正确性.以及良好的效率 ...

  7. shell单例-处理方案

    shell单例:当某一个shell脚本需要重复执行时(shell定时任务 etc),为了避免多个相同任务之间交叉,造成数据的混乱或者错误,需要脚本单例执行. 就是前一个进程执行时,后一个进程需要阻塞等 ...

  8. JAVASE(十七) 多线程:程序、进程、线程与线程的生命周期、死锁、单例、同步锁

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.程序.进程.线程的理解 1.1 概念 程序(program)是为完成特定任务.用某种语言编写的一组指 ...

  9. QT中实现应用程序的单例化

    一介绍 通过编写一个QSingleApplication类,来实现Qt程序的单例化,原文的作者是在Windows Vista + Qt4.4 下实现的,不过应用在其他平台上是没问题的.(本文是我在ht ...

随机推荐

  1. 转:深入了解Windows句柄

    深入了解Windows句柄到底是什么 转:http://blog.csdn.net/wenzhou1219/article/details/17659485 总是有新入门的Windows程序员问我Wi ...

  2. VMware Workstation的三种网络连接方式

    桥接模式(Bridged).NAT模式(地址转换模式).仅主机模式(Host-Only) 桥接模式就是将主机网卡与虚拟机的网卡利用虚拟网桥进行通信.在桥接的作用下,类似于把物理主机虚拟为一个交换机,所 ...

  3. CentOS7和CentOS6的主要区别

    了解一下就好 1.  文件系统的区别.CentOS6默认使用的是ext4的文件系统,而CentOS7使用的是xfs. 2.  硬盘默认调度算法不一样.CentOS6默认使用的是cfq,而CentOS7 ...

  4. JNDI Tomcat

    1.JNDI的诞生及简介简介 1)服务器数据源配置的诞生 JDBC阶段: 一开始是使用JDBC来连接操作数据库的: 在Java开发中,使用JDBC操作数据库的四个步骤如下: ①加载数据库驱动程序(Cl ...

  5. ANY和SOME 运算符

    在SQL中ANY和SOME是同义词,所以下面介绍的时候只使用ANY,SOME的用法和功能和ANY一模一样.和IN运算符不同,ANY必须和其他的比较运算符共同使用,而且必须将比较运算符放在ANY 关键字 ...

  6. ARC-100 E - Or Plus Max

    题面在这里! 我们如果可以求出 f[x] = max{ a[i] + a[j] , i!=j && i or j == x},那么就可以通过前缀max直接递推答案了. 但是这个玩意不是 ...

  7. BZOJ 3127 [Usaco2013 Open]Yin and Yang(树点分治)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3127 [题目大意] 给出一棵01边权树,求存在多少条路径,使得路径上0和1的数量相同, ...

  8. BZOJ 4236 JOIOJI(前缀和)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4236 [题目大意] 给出一个只包含三种字母的字符串,求出三种字母数量相等的最长子串 [ ...

  9. bootstrap中日历组件只显示年月

    大多数情况下日历组件我们使用的都是yyyy-mm-dd的日历格式,但是有时候需求不需要我们精确到日,而是最小单位到月份(yyyy-mm),网上找了很多方法,但是都没有我想要的效果,一些属性的设置都没有 ...

  10. opencv中SiftDescriptorExtractor所做的SIFT特征向量提取工作简单分析

    SiftDescriptorExtractor对应于SIFT算法中特征向量提取的工作,通过他对关键点周围邻域内的像素分块进行梯度运算,得到128维的特征向量.具体有如下几个操作: 0.首先,我们假设在 ...