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

Vla*_*mir 12 php mysql cron

说明

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

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

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

问题

我的问题是当Instant CallAPI正在进行中时,Cron Job可能还会尝试另一个调用,因为它尚未标记为成功,

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

我已经试图阻止这个问题

我尝试将In ProcessAPI调用存储在数据库表中,Status=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

 }
Run Code Online (Sandbox Code Playgroud)

但如果在同一时间发生Instant CallCron Job Call发生怎么办?他们都检查状态是否为0,然后将状态设置为1并执行API调用...

问题

  1. 我尝试过正确的方法来处理这个问题吗?

  2. 如果有很多电话(有时+ 500 /秒),我应该担心它们在确切的时间发生(我在上面的黄色报价中解释的问题)

Bounty之前更新

在PHP方面是不是真的有一种简单的方法来处理这种情况?如果没有,专家们认为哪种方式更好?下面是一些方法,但没有一个足够详细,没有任何一个有任何Downvotes/Upvotes.

PS对数据库有很多更新/插入,我不认为锁定是一个有效的想法,我不确定其余的想法.

小智 15

这正是Semaphore创建的原因.

在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.
Run Code Online (Sandbox Code Playgroud)

那么它们如何一起工作呢?首先,调用sem_get()来获取信号量的标识符.之后,您的一个进程将调用sem_acquire()来尝试获取信号量.如果它当前不可用,sem_acquire()将阻塞,直到另一个进程释放信号量.获取信号量后,您可以访问您使用它控制的资源.完成资源后,调用sem_release()以便另一个进程可以获取信号量.完成所有操作后,您已确保所有进程都不再需要信号量,您可以调用sem_remove()来完全删除信号量.

您可以在本文中找到有关此内容的更多信息和示例.


Jac*_*tal 5

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

SCRIPT START
LOCK FILE 'MYPROCESSFILE.LOCK'
DO SOMETHING I WANT
UNLOCK FILE 'MYPROCESSFILE.LOCK'
SCRIPT END
Run Code Online (Sandbox Code Playgroud)

因此,如果文件被锁定,第二个(重复的)进程将不会运行(将锁定/暂停/等待),直到文件被原始进程解锁。

编辑更新了 WORKING PHP 代码

<?php

    class Locker {

        public $filename;
        private $_lock;

        public function __construct($filename) {
            $this->filename = $filename;
        }

        /**
         * locks relevant file
         */
        public function lock() {
                touch($this->filename);
                $this->_lock = fopen($this->filename, 'r');
                flock($this->_lock, LOCK_EX);
        }

        /**
         * unlock above file
         */
        public function unlock() {
                flock($this->_lock, LOCK_UN);
        }

    }

    $locker = new Locker('locker.lock');
    echo "Waiting\n";
    $locker->lock();
    echo "Sleeping\n";
    sleep(30);
    echo "Done\n";
    $locker->unlock();

?>
Run Code Online (Sandbox Code Playgroud)


小智 1

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