脚本文件执行进程锁-防重复执行

本文介绍了如何使用文件进程锁避免PHP脚本并发执行导致的数据错乱问题。通过创建一个LogLock类,脚本在执行前后会检查并设置进程锁,确保同一时间只有一个实例在运行。当脚本结束或检测到假死进程时,会释放锁。测试用例展示了脚本如何在实际操作中生成和删除进程锁文件。
摘要由CSDN通过智能技术生成

【应用背景】

平时一些脚本,比如上篇文章【crontab通过shell脚本执行业务脚本】用到php的脚本,像【r_gap_basic_day.php】是用来实时统计当天游戏-渠道-广告位维度的注册活跃付费等基础数据的,crontab是设定的8分钟执行一次,那如果脚本有问题,导致上一次脚本执行没完,后面脚本又开始执行了,那就可能导致数据错乱问题。或者另外一种情况,想看当前数据,手动执行的时候恰好碰到定时任务也在执行,就可能出现冲突。这时候就需要一个机制,当脚本已经在执行了,就提示不再重复。本文要介绍的文件进程锁,就是这个机制的一种实现方式,主要逻辑就是:
1、php脚本执行的时候,调用进程锁类,把当前执行的进程id记录下来文件(也可以考虑记录到其它地方),当脚本执行完毕时候删除进程标识(这个很有必要,有些情况可能进程id结束后被系统复用,比如被一个常用进程用上了就一直存在锁着了)。
2、php脚本执行前增加一个监测,是否该文件的进程id还存在着,如果有就表示上一次执行还在,不能继续执行,否则继续
3、也可以优化下,针对一些假死进程(或者脚本bug没走到删除进程文件地方),比如进程锁太久了,一天还没释放,那大概率就是有问题的了,这时候可以直接清除。
4、最初也考虑过在脚本执行初期reids记录标识,在执行结束删除,但是当脚本中间退出就会导致没删除,以为一直锁住。后面才引入了进程id的概念,即使中间退出也可以通过进程id检验到是否在执行中

【进程锁类文件-loglock.php】

<?php
/*
   检查进程是否存在,存在返回1,不存在返回0
 */
class LogLock {
	private static $_instance = null;
	public function getlock($name) {
		$lock = $name.".lock";
		if (file_exists($lock)) {
			$check_pid = file_get_contents($lock);
			system("kill -CHLD " . $check_pid . " 1> /dev/null 2> /dev/null", $check_pid_return);
			if($check_pid_return == 1)
			{
				//存在已经假死的进程,或进程不存在
				return 0;
			}
			else {
				//已存在正在运行的进程
				return 1;
			}
		} else {
			//不存在锁文件
			return 0;
		}
	}
	/*
	   给当前进程上锁
	 */
	public function lockit($name)
	{
		$lock = $name.".lock";
		file_put_contents($lock,getmypid());
	}
	/*
	   删除进程锁
	 */
	public function unlock($name) 
	{	
		$lock = $name.".lock";
		@unlink($lock);
	}
/**
	 * 获取单例
	 *
	 * @return Sylogbase
	 */
	public static function getInstance()
	{
		if (self::$_instance instanceof self) {
			return self::$instance;
		}
		return new self();
	}
}

【测试用例】

类似下面测试用例【testLock.php】,脚本执行过程中,就会在lock日志目录下生成对应的进程锁文件testLock.php.lock(里面记录着运行的进程id),当脚本执行结束,就会把文件删除。

<?php
date_default_timezone_set('Asia/Shanghai');
include 'loglock.php';
$obj = new TestLock($_SERVER["PHP_SELF"]);
class TestLock
{
    public $logDir; //锁文件位置
    public $lockName; //锁文件名称

    /**
     *
     * @param string $lockName 对应脚本名,可作为锁文件名
     * @param string $param 脚本参数信息
     * @param int $timeType 脚本运行时间类型(1=实时,2=天一次,3=周一次,4=月一次)
     */
    public function __construct($lockName, $param = null, $timeType = 1)
    {
        $this->lockName = $lockName;
        $this->logDir = '/tmp/lock_log/';
        //判断文件进程锁
        if (!$this->checkLock()) {
            $this->writeLog($this->lockName . '已经在运行了!');
        }
        $this->setParamAndRun();
        $this->writeLog('结束运行' . $this->lockName, false);
        $this->unlock();
    }
    //设置参数和执行脚本逻辑代码
    public function setParamAndRun()
    {
        sleep(5);
        return;
    }
    /**
     * 判断是否可以执行该文件(是否已经在运行了)
     * @return true=可以执行,false=不能执行
     */
    public function checkLock(){
        $lockObj = LogLock::getInstance();
        $lock = $lockObj->getlock($this->logDir.$this->lockName);
        if ($lock) {
            return false;
        }else{
            $lockObj->lockit($this->logDir.$this->lockName);
            return true;
        }
    }
    /**
     * 执行完脚本删除lock文件
     */
    public function unlock(){
        $lockObj = LogLock::getInstance();
        $lock = $lockObj->unlock($this->logDir.$this->lockName);
    }
    /**
     * 记录日志
     * @return true=是,false=不是
     */
    public function writeLog($msg,$isExist=true){
        $nowTimeStr = date('Y-m-d H:i:s',time());
        $msg = '====='.$nowTimeStr.'====='.$msg."\n";
        echo $msg;
        if($isExist){
            exit;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值