PHP实现程序单例执行
一、场景描写叙述:
近期我们一块业务。须要不断的监听一个文件夹的变化。假设文件夹中有文件,则启动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实现程序单例执行的更多相关文章
- C#实现程序单例日志输出
对于一个完整的程序系统,一个日志记录是必不可少的.可以用它来记录程序在运行过程中的运行状态和报错信息.比如,那些不想通过弹框提示的错误,程序执行过程中捕获的异常等. 首先,在你的解决方案中,适当的目录 ...
- C#应用程序单例并激活程序的窗口 使其显示在最前端
public class SoftHelper { ///<summary> /// 该函数设置由不同线程产生的窗口的显示状态 /// </summary> /// <p ...
- Inno Setup安装程序单例运行
1.源起: KV项目下载底层升级包,老是报出升级文件占用问题,反复分析,不得其所. 今天突然发现同时启动多个升级程序实例,分析认为安装包同时被调用多次,引发实例访问文件冲突,导致此问题. 安装程序由I ...
- C# 应用程序单例(禁止多开) 获取.net版本号 以及 管理员权限
Mutex不仅提供跨线程的服务,还提供跨进程的服务.当在构造函数中为Mutex指定名称时,则会创建一个命名了的Mutex.其他线程创建Mutex时,如果指定的名称相同,则返回同一个互斥体,不论该线程位 ...
- 编写一个Singleton程序(单例)
public class Test { private static Test test = new Test(); private Test(){}//构造方法私有化 private static ...
- 23种设计模式之单例(Singleton Pattern)
单例 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例(eg:应对一些特殊情况,比如数据库连接池(内置了资源) 全局唯一号码生成器),才能确保它们的逻辑正确性.以及良好的效率 ...
- shell单例-处理方案
shell单例:当某一个shell脚本需要重复执行时(shell定时任务 etc),为了避免多个相同任务之间交叉,造成数据的混乱或者错误,需要脚本单例执行. 就是前一个进程执行时,后一个进程需要阻塞等 ...
- JAVASE(十七) 多线程:程序、进程、线程与线程的生命周期、死锁、单例、同步锁
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.程序.进程.线程的理解 1.1 概念 程序(program)是为完成特定任务.用某种语言编写的一组指 ...
- QT中实现应用程序的单例化
一介绍 通过编写一个QSingleApplication类,来实现Qt程序的单例化,原文的作者是在Windows Vista + Qt4.4 下实现的,不过应用在其他平台上是没问题的.(本文是我在ht ...
随机推荐
- CentOS 7.4 上如何安装 tomcat 9
本文将详细讲解在 CentOS 7.4 系统上如何安装tomcat 9,tomcat是没有32位和64位之分的. 创建tomcat的安装路径 首先在/usr/local/下建立一个tomcat的文件夹 ...
- linux 下 php 扩展
首先查看,php 原来安装的配置 /www/server/php/72/bin/php -i | grep configure (php 位置) 加上你的扩展 '--with-mcrypt' make ...
- html不识别<br/>,后台返回<br/>,前端不换行解决办法
今天编写页面,后台直接返回带有html格式的字符串,包含<br/>,前端以为要展示<br/>,将其解析为<br/>页面不换行 解决办法 后台将<br/> ...
- TensorFlow-GPU安装配置(win10+tensorflow1.6+CUDA9.0+cudnn7.0+python3.6+Visual Studio2013)
安装步骤: TensorFlow官网 tensorflow一般只能装在python3上,CUDA9.0搭配cudnn7.0,CUDA8.0搭配cudnn6.0 查看对应要安装的环境版本(因为会不断更新 ...
- 洛谷——P1476 休息中的小呆
P1476 休息中的小呆 题目描述 当大家在考场中接受考验(折磨?)的时候,小呆正在悠闲(欠扁)地玩一个叫“最初梦想”的游戏.游戏描述的是一个叫pass的有志少年在不同的时空穿越对抗传说中的大魔王ch ...
- Sd - Java多线程
1. Java中线程有哪些同步的方法? 在Java语言中,每个对象都有一个锁,一个线程可以通过关键字synchronized来申请获取某个对象的锁,关键字synchronized可以被用于方法(粗粒度 ...
- 对Webservice的理解
eb Service使用的是 SOAP (Simple Object Access Protocol)协议 soap协议只是用来封装消息用的.封装后的消息你可以通过各种已有的协 ...
- [BZOJ4813][CQOI2017]小Q的棋盘(DP,贪心)
4813: [Cqoi2017]小Q的棋盘 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 804 Solved: 441[Submit][Statu ...
- 【枚举约数】Gym - 101412A - Ginkgo Numbers
给你一堆定义,问你在那个定义下,<p,q>是不是素数.其实那堆定义都不用管,只要看最下面给你的提示即可. 根据,只要把m^2+n^2当一个整体,去枚举(p^2+q^2)的约数即可,然后再枚 ...
- mysql数据操作
了解:Mysql 账号相关 创建账号: 权限:user(所有库的权限)-->db(某个库的权限)-->table_priv(某张表的权限) -->columns_oriv(某个字段的 ...