PHP锁定/确保给定脚本在任何给定时间只运行一次

     2023-03-06     25

关键词:

【中文标题】PHP锁定/确保给定脚本在任何给定时间只运行一次【英文标题】:PHP locking / making sure a given script is only running once at any given time 【发布时间】:2012-01-31 16:55:54 【问题描述】:

我正在尝试编写一个 PHP 脚本,我想确保它在任何给定时间都只有一个实例在运行。所有这些关于锁定的不同方式、竞争条件等的讨论都让我感到沮丧。

我很困惑锁定文件是要走的路,还是信号量,还是使用 MySQL 锁,等等等等。

谁能告诉我:

a) 实现这个的正确方法是什么?

b) 指向一个 PHP 实现(或易于移植到 PHP 的东西?)

【问题讨论】:

脚本是在网络服务器还是命令行下运行? 如果它真的很重要......网络服务器。 可能是一个 mysql 锁,在这种情况下,脚本可以优雅地中止并说“已在使用中”。使用flock之类的东西会将脚本与apache本身锁定,并可能导致500个内部错误等等。 【参考方案1】:

一种方法是将 php 函数 flock 与一个虚拟文件一起使用,该文件将充当看门狗。

在我们的工作开始时,如果文件引发 LOCK_EX 标志,则可以退出或等待。

PHP 群文档:http://php.net/manual/en/function.flock.php

对于本示例,必须首先创建一个名为 lock.txt 的文件。

示例1,如果另一个孪生进程正在运行,它将正常退出,无需重试,并给出状态消息。

如果文件 lock.txt 不可访问,它会抛出错误状态。

<?php

$fp = fopen("lock.txt", "r+");

if (!flock($fp, LOCK_EX|LOCK_NB, $blocked)) 
    if ($blocked) 

        // another process holds the lock
        echo "Couldn't get the lock! Other script in run!\n"; 

    
    else 
        // couldn't lock for another reason, e.g. no such file
        echo "Error! Nothing done.";
    

else 
    // lock obtained
    ftruncate($fp, 0);  // truncate file

    // Your job here 
    echo "Job running!\n";
    
    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock



fclose($fp); // Empty memory

示例 2FIFO(先进先出):我们希望进程等待队列之后的执行,如果有的话:

<?php

$fp = fopen("lock.txt", "r+");

if (flock($fp, LOCK_EX))   // acquire an exclusive lock
    ftruncate($fp, 0);      // truncate file

    // Your job here 
    echo "Job running!\n";

    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock


fclose($fp);

也可以将 fopen 置于 x 模式,通过在脚本结束时创建和擦除文件。

创建和打开仅供写作;将文件指针放在 文件的开头。如果文件已经存在,则 fopen() 调用 将返回 FALSE 失败

http://php.net/manual/en/function.fopen.php


但是,进入 Unix 环境,为了进行微调,我发现将每个带有 getmypid() 的后台脚本的 PID 列出到数据库或单独的 JSON 文件中更容易。

当一个任务结束时,脚本负责在这个文件中声明他的状态(eq:success/failure/debug infos等),然后删除他的PID。在我看来,这允许以更简单的方式创建管理工具和守护进程。并在必要时使用posix_kill() 从 PHP 中杀死一个 PID。

微服务是使用类 Unix 管道组成的。 服务可以调用服务。 https://en.wikipedia.org/wiki/Microservices


另请参阅: Prevent PHP script using up all resources while it runs?

【讨论】:

【参考方案2】:
// borrow from 2 anwsers on ***
function IsProcessRunning($pid) 
    return shell_exec("ps aux | grep " . $pid . " | wc -l") > 2;


function AmIRunning($process_file) 
    // Check I am running from the command line
    if (PHP_SAPI != 'cli') 
        error('Run me from the command line');
        exit;
    

    // Check if I'm already running and kill myself off if I am
    $pid_running = false;
    $pid = 0;
    if (file_exists($process_file)) 
        $data = file($process_file);
        foreach ($data as $pid) 
            $pid = (int)$pid;
            if ($pid > 0 && IsProcessRunning($pid)) 
                $pid_running = $pid;
                break;
            
        
    
    if ($pid_running && $pid_running != getmypid()) 
        if (file_exists($process_file)) 
            file_put_contents($process_file, $pid);
        
        info('I am already running as pid ' . $pid . ' so stopping now');
        return true;
     else 
        // Make sure file has just me in it
        file_put_contents($process_file, getmypid());
        info('Written pid with id '.getmypid());
        return false;
    


/*
 * Make sure there is only one instance running at a time
 */
$lockdir = '/data/lock';
$script_name = basename(__FILE__, '.php');
// The file to store our process file
$process_file = $lockdir . DS . $script_name . '.pid';

$am_i_running = AmIRunning($process_file);
if ($am_i_running) 
    exit;

【讨论】:

【参考方案3】:

使用信号量:

$key = 156478953; //this should be unique for each script
$maxAcquire = 1;
$permissions =0666;
$autoRelease = 1; //releases semaphore when request is shut down (you dont have to worry about die(), exit() or return
$non_blocking = false; //if true, fails instantly if semaphore is not free

$semaphore = sem_get($key, $maxAcquire, $permissions, $autoRelease);
if (sem_acquire($semaphore, $non_blocking ))  //blocking (prevent simultaneous multiple executions)

    processLongCalculation();

sem_release($semaphore);

见:

https://www.php.net/manual/en/function.sem-get.php

https://www.php.net/manual/en/function.sem-acquire.php

https://www.php.net/manual/en/function.sem-release.php

【讨论】:

【参考方案4】:

您可以选择最适合您的项目的解决方案,实现该解决方案的两种简单方法是文件锁定或数据库锁定。

有关文件锁定的实现,请查看http://us2.php.net/flock

如果您已经使用数据库,请创建一个表,为该脚本生成已知令牌,将其放在那里,然后在脚本结束后将其删除。为避免出现错误问题,您可以使用到期时间。

【讨论】:

您确定这是一种安全的处理方式吗? PHP.net 上的 cmets 有几个条目表明它可能会引入竞争条件。【参考方案5】:

也许这对你有用,

http://www.electrictoolbox.com/check-php-script-already-running/

【讨论】:

【参考方案6】:

如果你在linux上使用php,我认为最实用的方法是:

<?php
 if(shell_exec('ps aux | grep '.__FILE__.' | wc  -l')>3)
    exit('already running...');
 
?>

另一种方法是使用文件标志和退出回调, 退出回调将确保文件标志将在任何情况下重置为 0 php 执行结束也是致命错误。

<?php
function exitProcess()
  if(file_get_contents('inprocess.txt')!='0')
    file_put_contents('inprocess.txt','0');  
  


if(file_get_contents('inprocess.txt')=='1')
  exit();


file_put_contents('inprocess.txt','1');
register_shutdown_function('exitProcess');

/**
execute stuff
**/
?>

【讨论】:

在任何给定时间运行 5 个脚本

】在任何给定时间运行5个脚本【英文标题】:Have5scriptsrunningatanygiventime【发布时间】:2013-07-0801:41:48【问题描述】:我有一个启动90个不同PHP脚本的bash脚本(在CentOS6.4下运行),即。#!/bin/bashphppath1/some_job_1.php&phppath2/some_job_2.... 查看详情

如何确保我的 bash 脚本尚未运行?

...情况下,我希望新的运行退出.我不想只依赖/tmp中的一个锁定文件。我想确保在我尊重锁定文件(或其他任何东西)之前该进程正在实际运 查看详情

如何确保脚本运行一次且只运行一次

】如何确保脚本运行一次且只运行一次【英文标题】:Howtomakesureascriptrunsonceandonlyonce【发布时间】:2012-01-0815:57:33【问题描述】:我正在编写一个小部件模板,它将包含在安装它的页面中。一个人可能会在一个页面中安装多个相... 查看详情

如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?

】如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?【英文标题】:Howtocheckincommand-lineifagivenfileordirectoryislocked(usedbyanyprocess)?【发布时间】:2012-05-1802:09:38【问题描述】:在尝试对此类文件执行任何操作之前,... 查看详情

PHP GD - 围绕任何给定点裁剪图像

】PHPGD-围绕任何给定点裁剪图像【英文标题】:PHPGD-cropimagearoundanygivenpoint【发布时间】:2014-09-1513:33:09【问题描述】:我有以下脚本,使用php的gd在中心点成功裁剪和图像:list($source_width,$source_height,$source_type)=getimagesize($img_path);... 查看详情

给定时间内的 Cron 作业和随机时间

】给定时间内的Cron作业和随机时间【英文标题】:Cronjobsandrandomtimes,withingivenhours【发布时间】:2012-02-2109:30:54【问题描述】:我需要能够在完全随机的时间每天运行20次PHP脚本。我还希望它只在上午9点到晚上11点之间运行。我熟... 查看详情

从特定时区的给定日期起每 14 天执行一次作业的 Cron 脚本

】从特定时区的给定日期起每14天执行一次作业的Cron脚本【英文标题】:CronScripttoexecuteajobevery14daysfromagivendateinspecifictimezone【发布时间】:2021-06-2600:53:42【问题描述】:我想从特定日期和时区开始每14天在CRON中执行一次作业。例... 查看详情

是否有任何理由锁定队列?

】是否有任何理由锁定队列?【英文标题】:Isthereanyreasontolockaqueue?【发布时间】:2018-10-3017:16:46【问题描述】:我只是想知道是否有任何理由要锁定队列。我正在开发一个应用程序,该应用程序具有多个读取和写入数据库的线... 查看详情

如何按名称限制运行 Celery 任务的最大数量

...致服务器在尝试一次处理太多文件时耗尽CPU和内存。我想确保在任何给定时间只运行此类任务的N 查看详情

并发管道的概念是啥(Azure Pipeline 中的一个并行作业允许您在任何给定时间运行单个构建或发布作业)?

...的概念是啥(AzurePipeline中的一个并行作业允许您在任何给定时间运行单个构建或发布作业)?【英文标题】:Whatistheconceptofconcurrentpipelines(OneparalleljobinAzurePipelineletsyourunasinglebuildorreleasejobatanygiventime)?并发管道的概念是什么(Azure... 查看详情

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现偶数次。找出那个只出现了一次的元素。

如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。按位异或的3个特点:  (1)0^0=0,0^1=1   0异或任何数=任何数  (2)1^0=1,1^1=0  1异或任何数-任何数取反  (3)任何数异... 查看详情

rc.d 脚本默认给定一个“开始”参数?

】rc.d脚本默认给定一个“开始”参数?【英文标题】:rc.dscriptgivena"start"argumentbydefault?【发布时间】:2018-10-1423:32:45【问题描述】:我的目标是制作一个bash脚本服务,它在运行级别为5时创建一个文件,并在运行级别为3... 查看详情

三个表之间的连接表的优缺点,在任何给定时间只有两个表

】三个表之间的连接表的优缺点,在任何给定时间只有两个表【英文标题】:Prosandconsofajunctiontablebetweenthreetables,onlytwoofwhichatanygiventime【发布时间】:2011-11-1816:25:51【问题描述】:我将在三个表之间建立多对多关系,但该关系一... 查看详情

即使我在android中的给定时间间隔内请求更新,融合位置api的OnLocationResult也只调用一次

】即使我在android中的给定时间间隔内请求更新,融合位置api的OnLocationResult也只调用一次【英文标题】:OnLocationResultoffusedLocationapiisonlycalledonceevenwhenihaverequestedupdatesingiventimeintervalinandroid【发布时间】:2018-10-0812:12:28【问题描述... 查看详情

如何确保我的 Apache Spark 设置代码只运行一次?

】如何确保我的ApacheSpark设置代码只运行一次?【英文标题】:HowdoIensurethatmyApacheSparksetupcoderunsonlyonce?【发布时间】:2019-11-1415:16:30【问题描述】:我正在使用Scala编写一个Spark作业,它读取S3上的parquet文件,进行一些简单的转换... 查看详情

如何确保给定的未来在测试中首先完成?

】如何确保给定的未来在测试中首先完成?【英文标题】:Howtomakesureagivenfuturecompletesfirstintests?【发布时间】:2021-06-2006:27:27【问题描述】:我正在为函数bar编写测试:defbar(fut1:Future[Int],fut2:Future[Int],fut3:Future[Int]):Future[Result]=???ba... 查看详情

在给定时间运行我的 kivy 应用程序的 python 代码

】在给定时间运行我的kivy应用程序的python代码【英文标题】:Runmypythoncodeofkivyappatgiventime【发布时间】:2020-12-0606:51:18【问题描述】:当用户选择0-99之间的任何随机数时,我制作了一个基本应用程序,并且在晚上8点整,这个应... 查看详情

一个php程序,同一时刻被请求多次,怎么让它只运行一次?

...决.不过可能是走弯路了.只给你一个思想<?phpsession_start给定一个随机数,具体范围自己定sleep(随机数)//不宜过大if(!$_SESSION[])写sessionelsereturn就好了参考技术A用session,记录就行了 参考技术B记录监控程序的访问ip。追问IP经常会换追... 查看详情