每5分钟运行一个PHP脚本并避免竞争条件。

8
我有一个需要每5分钟运行一次的php脚本。目前我使用cron job来运行它(效果很好),但我的主机只允许最少15分钟。所以我的问题是,我能否利用访问者触发每5分钟运行一次php脚本吗?我可以轻松地记录上次运行时间,并根据经过的时间重新运行它。然而,我担心会出现竞态条件。重要的是,脚本每5分钟只运行一次。我的脚本需要大约60秒才能运行。在此期间,它会写入几个文件。如果脚本运行超过一次,它将破坏文件。另外,如果我在10分钟内没有访问者,那么在下一个访问者到达时运行一次就可以了。是否有一些标准的方法来完成这个任务呢?谢谢!

我不确定我理解问题。是你的主机不允许cron作业每15分钟执行一次以上吗? - Icode4food
1
标准的方法是使用没有对cron施加不良、武断规则的Web主机。谁会这么做呢? - Erik
澄清一下,是的,主机不允许 cron 作业每15分钟运行一次以上。所涉及的主机是 HostGator,并且我对他们的服务感到满意。您可以在此处查看他们的政策:http://support.hostgator.com/articles/cpanel/what-are-cron-jobs - Imbue
1
不直接回答你的问题,但是你可以考虑像Webkeepers或其他一些愚蠢便宜的VPS(每月7美元)这样的主机,以后就再也不用担心HG的傻事了。 - Erik
@Erik,哇,我不知道它们这么便宜。这实际上是一个非常好的建议。如果我这样做,我还可以放弃PHP...感谢您的建议。现在只要我能找到迁移一切的动力就好了... - Imbue
4个回答

6

您考虑过只需让脚本运行一个无限循环,使用sleep在每次迭代之间等待5分钟吗?

for (;;)
{
  perform_actions();
  sleep(300);
}

或者,您可以拥有一个文件(例如is_running),并在脚本开始时对其进行独占lock,并在结束时释放。至少这样,您不会做出任何破坏性的事情。

您也可以将这两个解决方案结合起来使用。

$fp = fopen("is_running", "r+");

/* is it already running? */
if (! flock($fp, LOCK_EX | LOCK_NB)) return;

for (;;)
{
  perform_actions();
  sleep(300);
}

然后仍然每15分钟运行cron作业。如果进程仍在运行,它将退出,否则它将重新启动并继续每5分钟更新。


这似乎是一个不错的想法和解决方法。我猜我需要调用php的set_time_limit。然后我希望我的主机不会自动终止长时间运行的进程... 但是,这似乎是我最好的选择。谢谢。 - Imbue
1
set_time_limit 是你需要调整超时时间的函数。http://php.net/manual/en/function.set-time-limit.php 我建议每次运行脚本时将其设置为大约 330,这样你就不必担心了。 - Icode4food
2
只是想让你们知道,主机允许我的脚本运行了大约26个小时,然后才杀掉它。不久之后,cron又启动了另一个脚本,发现文件已经解锁,我又回到了工作状态。有一件事让我感到担心的是,如果主机在脚本正在写入数据时将其杀死,那么数据可能会损坏,直到cron有机会运行另一个脚本。换句话说,每年可能会有几次,我需要为大约5-10分钟提供损坏的数据。 - Imbue
@Imbue,也许你不应该将它变成一个无限循环,而是让它执行一次,sleep(300),再执行一次并结束。这样它每10分钟运行2次,cron在5分钟后启动它,有效地每5分钟运行一次。如果在写入数据时崩溃,如果你使用的是平面文件,可以通过操作临时文件来处理,然后在所有工作完成后将它们重命名为原来的文件。如果是数据库,有其他保护措施来防止损坏。 - Brandon Horsley

4

这是一个无奈的答案(针对的是网络服务提供商,而不是发帖者)。可以设置12个定时任务,每个任务都调用同一脚本,在不同的5分钟时间点运行,每小时运行一次。

00 * * * * root echo "run at :00 of every hour"
05 * * * * root echo "run at :05 of every hour"
10 * * * * root echo "run at :10 of every hour"

直到:55,等等等。但我仍然坚持我的原评论 - 找一家新的互联网服务提供商 :)

1
我相信这会起作用,而且可能是一个不错的解决方案,但我担心如果我的主机注意到它会发生什么。 - Imbue
3
@Imbue - 所以你认为你的主机会更善意地看待一个无限循环? - Peter Ajtai
我认为一个几乎全部时间都在睡眠中的无限循环会更不易被注意到,或许也会稍微低调一些。但这并不确定,实际效果难以预测。 - Imbue
我真的怀疑 Host Gator 上的 PHP 设置是否允许脚本无限运行。而且我越想,我的解决方案可能也会失败。你可以使用基于 Web 的定时任务 - 有一个需要花费 $10/年,它将通过 http 调用您的脚本,但我忘记了它的名字。 :) - Erik
好的,我现在正在测试它。我可以在CPanel中看到进程正在运行,它使用0%的CPU(在睡眠时)。我只是记录每次迭代的时间。实际上,脚本只需要运行10分钟。如果cron设置为15分钟,则脚本会执行开始->工作->等待5分钟->工作->等待5分钟->工作->退出。然后cron在5分钟后启动。 - Imbue
显示剩余2条评论

1

如果您无法执行@Brandon建议的操作,我建议以我编写PHP守护进程时所采用的方式来处理(这不是最好的解决方案,但我几乎被迫这样做)。

在我的情况下,脚本也访问了一个(日志)文件并对其进行了处理,然后将结果插入数据库。为了确保不会同时运行两个文件,我创建了一个“状态”文件,在该文件上脚本获取了锁,如果无法这样做,则会优雅地失败。

$fh = fopen('status_file', 'w');

/**
 * LOCK_NB is required because otherwise your script would stall until
 * a lock is aquired, queing a bunch of scripts.
 */
if(!flock($fh, LOCK_EX | LOCK_NB)) {
  exit 1; // our job is done here
}

0

访问者是否可以启动此脚本的答案是肯定的。当访问者进入页面时,您可以运行脚本。您需要存储开始时间,还需要一个正在运行的属性。这应该避免在尝试更新数据时出现任何冲突。我还会添加一个邮件警告字段,如果运行时间超过最大时间,您可以使用它。然后,您可以让脚本向您发送警告电子邮件,告知您的脚本已经超过了最大运行时间。我个人将这些状态保存在数据库中,但它们也可以存储在文件中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接