API调用(到另一个服务),通常需要10-20秒才能响应,存储在数据库中,
存储后,系统会立即尝试使用API向用户显示结果,但可能会失败(并显示失败但我们会自动重试),因此还有{{1}设置为每30秒运行一次并再次尝试(失败)查询。
如果API返回成功(无论是即时使用还是使用Cron Job),该标志在数据库中更改为成功,并且不会再次运行。
我的问题是在Cron Job
到API的过程中,Instant Call
也可能会尝试另一个调用,因为它尚未标记为成功,
同样在极少数情况下,当前一个Cron作业正在进行时,下一个Cron作业可能会再次运行该代码。
我尝试将Cron Job
API调用存储在带有In Process
的数据库表中,并在API调用成功时将其删除,或者如果失败则将状态设置为0,
Status=1
但如果
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 }
和Instant Call
恰好同时发生怎么办?它们都检查状态是否为0,然后将状态设置为1并执行API调用...
我尝试过正确的方法来处理这个问题吗?
如果有很多电话(有时+ 500 /秒),我应该担心它们会在确切的时间发生(我在上面的黄色报价中解释过的问题)
在PHP方面是不是真的有一种简单的方法来处理这种情况?如果没有,专家们认为哪种方式更好?下面是一些方法,但没有一个方法足够详细,没有任何一个有任何Downvotes / Upvotes。
P.S。对数据库有很多更新/插入,我不认为锁定是一个有效的想法,我不确定其余的想法。
答案 0 :(得分:13)
这正是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.
那么它们如何一起工作?首先,调用sem_get()来获取信号量的标识符。之后,您的一个进程将调用sem_acquire()来尝试获取信号量。如果它当前不可用,sem_acquire()将阻塞,直到信号量被另一个进程释放。获取信号量后,您可以访问您使用它控制的资源。完成资源后,调用sem_release()以便另一个进程可以获取信号量。完成所有操作后,您已确保所有进程都不再需要信号量,您可以调用sem_remove()来完全删除信号量。
您可以在this article。
中找到有关此内容的更多信息和示例答案 1 :(得分:4)
我在脚本中做的是 (伪代码)
SCRIPT START
LOCK FILE 'MYPROCESSFILE.LOCK'
DO SOMETHING I WANT
UNLOCK FILE 'MYPROCESSFILE.LOCK'
SCRIPT END
因此,如果文件被锁定,则第二个(重复的)进程将不会运行(将锁定/暂停/等待)UNTIL文件被原始进程解锁。
使用WORKING PHP代码更新了EDIT
<?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();
?>
答案 2 :(得分:2)
这里需要一个正确的排队解决方案。您可以使用队列表和表锁自己实现它,以避免不同的进程获取相同的作业。
所以你可以从队列表中选择这样的任务:
LOCK TABLES table WRITE;
SELECT * FORM table WHERE status = 0 LIMIT 1;
set status = 1 for the selected row
UNLOCK TABLES;
锁定表格可确保其他进程不会执行SELECT,也不会从表格中选取相同的行。
将作业插入队列就像这样简单:
INSERT INTO table (job_id, status) VALUES(NULL, status);
处理完成后删除作业:
DELETE FROM table WHERE job_id = 12345;
答案 3 :(得分:1)
在每个cron作业开始检查锁文件是否存在,如果在api进程完成后取消该文件,如果不是某个临时目录中的crate lock文件则退出退出。
答案 4 :(得分:1)
既然你应该知道cron运行的时间(比如说每5分钟一次),那么对于你的用户请求的函数,你可以检查一下系统时间是否恰好是应该运行cron的时间吗?这样做可以防止它们在同一时间运行。
答案 5 :(得分:1)
我在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_count
做事。 basename()
检查是为了确保我不会捕捉到special_archiver.php
或其他任何可能存在的内容。您也可以检查完整路径。
答案 6 :(得分:0)
Semaphores可以安装在php中,对于内核级别的信号控制,它将以原子方式控制进程锁定。 Unix旨在将此机制与其他方法(如signals)一起用于进程间通信。不确定你是否需要那么复杂。
它可能会查看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
它应该足够快,因为创建文件句柄(没有内容)转换为简单的系统调用。如果不是,请在bash shell中尝试。
答案 7 :(得分:0)
我不知道这是否是一个好方法:
git push otherproject mybranch:master
Cron职位:
脚本启动
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)
然后检查最后的SELECT结果
SELECT in_use_by FROM temp_queue ORDER_BY id ASC LIMIT 1;
if results != 0 return;
INSERT INTO temp_queue SET query_id=SOME_ID, in_use_by = 'cron';
SELECT in_use_by FROM temp_queue ORDER_BY id ASC LIMIT 1;
执行结束时:
if in_use_by == 'cron' continue
else return
API工作:
脚本启动
DELETE FROM temp_queue WHERE query_id=SOME_ID
然后检查最后的SELECT结果
SELECT in_use_by FROM temp_queue ORDER_BY id ASC LIMIT 1;
if results != 0 return;
INSERT INTO temp_queue SET query_id=SOME_ID, in_use_by = 'api';
SELECT in_use_by FROM temp_queue ORDER_BY id ASC LIMIT 1;
执行结束时:
if in_use_by == 'api' continue
else return
如果Cron Job&amp; API尝试在同一时间调用查询?他们都将使用query_id = SOME_ID检查第一个写入的行,因此只有1个继续。
是的,很多选择,插入和删除。但它确实有效。
你们对此有何看法?