Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP:如何防止代码的多次执行(如果已经在执行中) 解释_Php_Mysql_Cron - Fatal编程技术网

PHP:如何防止代码的多次执行(如果已经在执行中) 解释

PHP:如何防止代码的多次执行(如果已经在执行中) 解释,php,mysql,cron,Php,Mysql,Cron,数据库中存储了一个API调用(对另一个服务的调用),通常需要10-20秒才能响应 存储后,系统将立即尝试使用API向用户显示结果,但可能会失败(并显示失败,但我们将自动重试),因此还有一个Cron作业设置为每30秒运行一次,并再次尝试(失败的)查询 如果API返回success(无论是即时使用还是使用Cron作业),则该标志在数据库中更改为success,并且不会再次运行 问题 我的问题是,当对API的即时调用正在进行时,Cron作业可能还会尝试另一个调用,因为它尚未标记为成功 同样在极少数情况

数据库中存储了一个API调用(对另一个服务的调用),通常需要10-20秒才能响应

存储后,系统将立即尝试使用API向用户显示结果,但可能会失败(并显示失败,但我们将自动重试),因此还有一个
Cron作业
设置为每30秒运行一次,并再次尝试(失败的)查询

如果API返回success(无论是即时使用还是使用Cron作业),则该标志在数据库中更改为success,并且不会再次运行

问题 我的问题是,当对API的
即时调用
正在进行时,
Cron作业
可能还会尝试另一个调用,因为它尚未标记为成功

同样在极少数情况下,当前一个Cron作业正在进行时,下一个Cron作业可能会再次运行代码

我已经试图阻止这个问题 我尝试将
进程中的
API调用存储在
状态=1
的数据库表中,并在API调用成功时将其删除,或者在调用失败时将状态设置为0

 if ($status === 0)
 {
     
     // Set Status to 1 in Database First (or die() if database update failed)
     
     // Then Call The API

     // If Failed Set Status to 0 so Cron Job can try again
     
     // If Successful Change Flag to success and remove from queue

 }
     
但是如果
即时调用
Cron作业调用
同时发生会怎样?他们都检查状态是否为0,然后都将状态设置为1并执行API调用

问题
  • 我所尝试的是正确的处理方法吗

  • 如果有很多电话(有时+500/秒),我是否应该担心电话发生在准确的时间(我在上面的黄色引号中解释的问题)

  • 悬赏前更新 在PHP端是否真的有一种简单的方法来处理此类情况?如果没有,专家认为哪种方式更好?以下是一些方法,但没有一种方法足够详细,也没有一种方法有任何反对票/赞成票


    另外,数据库有许多更新/插入,我不认为锁定是一个有效的想法,我不确定其余的想法。

    在每个cron作业开始时,检查锁定文件是否存在,如果退出,如果不退出,请在api进程完成后将锁定文件装入某个临时目录中,取消链接该文件。

    因为您应该知道cron将运行的时间(比如每5分钟一次)那么,对于用户请求的函数,您是否可以检查系统时间是否正好是cron应该运行的时间?这至少会阻止他们在同一时间跑步

    我在Linux上使用它来查看在需要避免多次执行时脚本是否正在运行:

    $output = array();
    exec('pgrep -fl the_script.php', $output);
    
    然后扫描
    $output
    ,确定它是否已经在进行中

    例如,下面是现有代码的复制/粘贴:

    $exec_output = array();
    exec('pgrep -fl archiver.php', $exec_output);
    $pid_count = 0;
    foreach ($exec_output as $line) {
        $parts = explode(' ', $line);
        if (basename($parts[2]) == 'archiver.php') $pid_count++;
    }
    
    然后根据
    $pid\u count
    进行操作。
    basename()
    检查是为了确保我没有捕捉到像
    special\u archiver.php
    或任何可能存在的东西。您还可以检查完整路径。

    可以安装在php中,对于内核级别的信号控制,它将自动控制进程锁定。Unix被设计为将此机制与其他方法(如进程间通信)一起使用。我不确定你是否需要那么老练

    它可以通过查看ps-ef的输出来工作,但它可能受到系统负载和进程优先级的影响。您可能会发现它使用数据库标志工作,但为什么要增加开销呢?数据库可能会变得很忙

    我认为,当您希望每秒进行500次检查时,一个简单的文件可能不太容易出现延迟问题

    e、 g.如果cron脚本以

    if ( ! -f otherprocessisrunning)
    then
       // create/open the file
       > cronprocessisrunning
    
       // when cron process finishes
       // it removes the cronprocessisrunning file
       rm -f cronprocessisrunning
    else 
       sleep for 2 minutes
       call this function
    fi
    
    另一个脚本在php中的行为与此相同

    if (! file_exist(cronprocessisrunning))
        > otherprocessisrunning
        start the other process
        when it is finished, remove otherprocessisrunning
    endif
    
    它应该足够快,因为创建文件句柄(没有内容)转化为一个简单的系统调用。如果不是,请在bashshell中尝试。

    这正是为创建的原因

    在php中,它可以按以下方式使用: 在PHP中使用信号量实际上非常简单。只有4个信号量函数:

    sem_acquire() – Attempt to acquire control of a semaphore.
    sem_get() – Creates (or gets if already present) a semaphore.
    sem_release() – Releases the a semaphore if it is already acquired.
    sem_remove() – Removes (deletes) a semaphore.
    
    那么它们是如何协同工作的呢

  • 首先,调用
    sem\u get()
    获取信号量的标识符
  • 之后,您的一个进程将调用
    sem\u acquire()
    ,尝试获取信号量。如果当前不可用,
    sem_acquire()
    将阻塞,直到另一个进程释放信号量
  • 一旦信号量被获取,您就可以访问您用它控制的资源
  • 使用完资源后,调用
    sem\u release()
    ,以便另一个进程可以获取信号量
  • 当所有这些都说了又做了,并且您已经确保您的进程不再需要该信号量时,您可以调用
    sem\u remove()
    来完全删除该信号量

  • 您可以在中找到有关此问题的更多信息和示例。

    您需要一个合适的排队解决方案。您可以使用队列表和表锁自己实现它,以避免不同的进程拾取相同的作业

    因此,您可以从队列表中拾取任务,如下所示:

    LOCK TABLES table WRITE;
    SELECT * FORM table WHERE status = 0 LIMIT 1;
    set status = 1 for the selected row
    UNLOCK TABLES;
    
    锁定表将确保其他进程不执行选择,也不从表中拾取同一行

    将作业插入队列非常简单,如下所示:

    INSERT INTO table (job_id, status) VALUES(NULL, status);
    
    处理完成后删除作业:

    DELETE FROM table WHERE job_id = 12345;
    

    我不知道这是否是个好办法:

    temp_queue Table
    -----------------------
    id --> Int, Index, Autoincrement
    query_id --> Int (your query ID or something to identificate a specific query)
    in_use_by --> varchar (cron or api)
    
    Cron作业:

    脚本开始

    然后检查最后的选择结果

    if in_use_by == 'cron' continue
    else return
    
    if in_use_by == 'api' continue
    else return
    
    执行结束时:

    DELETE FROM temp_queue WHERE query_id=SOME_ID
    
    DELETE FROM temp_queue WHERE query_id=SOME_ID
    
    API作业:

    脚本开始

    然后检查最后的选择结果

    if in_use_by == 'cron' continue
    else return
    
    if in_use_by == 'api' continue
    else return
    
    执行结束时:

    DELETE FROM temp_queue WHERE query_id=SOME_ID
    
    DELETE FROM temp_queue WHERE query_id=SOME_ID
    

    如果Cron作业和API试图同时调用查询,会发生什么情况?它们都将检查query\u id=SOME\u id的第一个写入行,因此只有1个具有continue

    是的,有很多选择、插入和删除。但它是有效的

    你们怎么看?

    我在脚本中做的是 (伪代码)