需要很长执行时间的 php 脚本的 Cron 作业
Posted
技术标签:
【中文标题】需要很长执行时间的 php 脚本的 Cron 作业【英文标题】:Cron job for php script that requires VERY long execution time 【发布时间】:2011-05-04 05:20:07 【问题描述】:我有一个 php 脚本作为 cron 作业运行,它执行一组简单的任务,这些任务为数据库中的每个用户循环,大约需要 30 分钟才能完成。此过程每小时开始一次,并且需要尽可能快速和高效。我遇到的问题就像任何服务器脚本一样,执行时间各不相同,我需要找出最佳的 cron 时间设置。
如果我每分钟运行一次 cron,我需要在该分钟结束前 20 秒停止脚本的最后一个循环,以确保当前循环及时完成。在一个小时的过程中,这会浪费很多时间。
我想知道简单地删除 php 执行时间限制并每小时运行一次脚本并让它运行完成是否是一个坏主意......这是一个坏主意吗?
【问题讨论】:
您的 cron 作业是调用运行 PHP 的网站还是从命令行使用 PHP?我不完全理解第二段——你为什么停止处理并每分钟重新开始? 我想如果你解释一下工作需要做什么,你可能会在这里得到一些更有成效的答案。可能有更有效的方法来做到这一点。 如果使用 wget 调用长时间运行的 cron 作业,请考虑使用 -t 1 将尝试次数设置为 1,以便仅调用一次。 【参考方案1】:假设您希望尽快完成工作,请不要使用 cron。 Cron 适用于需要在特定时间发生的事情。它经常被滥用来模拟一个理想情况下会在工作出现时立即处理工作的后台进程。您可能应该编写一个连续运行的守护程序。 (注意:您还可以查看消息/工作队列类型系统,也有很好的库可以做到这一点)
您可以使用pcntl functions 从头开始编写守护程序(因为您不关心多个工作进程,所以super-easy 可以让一个进程在后台运行。),或者作弊并编写一个脚本永远运行并通过 screen 运行它,或者利用一些可靠的库代码,如 PEAR 的 System:Daemon 或 nanoserv
一旦处理好守护进程,您真正关心的就是拥有一个永远运行的循环。您需要注意您的脚本不会泄漏内存或消耗太多资源。
通常,您可以执行以下操作:
<?PHP
// some setup code
while(true)
$todo = figureOutIfIHaveWorkToDo();
foreach($todo as $something)
//do stuff with $something
//remember to clean up resources so you don't leak memory!
usleep(/*some integer*/);
usleep(/* some other integer */);
而且效果会很好。
【讨论】:
如果 php.ini 文件的 max_execution_time = 30 怎么办?,脚本将在 30 秒后终止。 @KiranManiya - 不!你会这么想,但是 php-cli 忽略了这个值。写一个简单的测试脚本,自己看看。【参考方案2】:除了设置max_execution_time
,您还可以使用set_time_limit() 在每个循环中重置计数器。这将确保您的脚本永远不会耗尽时间,除非当前循环中存在严重问题(并且花费的时间超过 max_execution_time)。
基本上,这应该让您的脚本在需要时运行,同时在两次set_time_limit()
调用之间给它一个 30 秒的超时时间。
【讨论】:
这是一个很好的建议。我不知道你为什么没有足够的选票。【参考方案3】:将时间限制设置为 0 并让它完成工作是基于 PHP 的 cronjobs 的典型做法(根据我的经验),但这也是您应该问自己几个重要问题的时候,例如“我应该重写这份工作是用编译语言写的吗?”和“我是否将我所有的工具(数据库等)发挥到最大效率?”
也就是说,也许比完全取消时间限制更好的是将其设置为您真正想要的上限。如果这意味着 48 分钟,那么set_time_limit(48 * 60);
【讨论】:
【参考方案4】:我真的认为您不应该将超时设置为0,那只是在找麻烦。最多设置为 59*60 秒,但设置为 0 可能会导致安全问题,如果脚本挂起,它将几乎永远挂起,直到服务器主机停止执行。这样做被认为是不好的做法。
【讨论】:
【参考方案5】:过去,我曾将 php 命令行界面用于类似的长时间运行的任务。您可能不想取消任何请求的执行时间限制。
【讨论】:
【参考方案6】:如果不太可能需要一个多小时,这听起来是个好主意。但是请注意,错误的错误可能是一个非常好的方法,它会比预期的花费更长的时间..
为避免各种令人讨厌的问题,您应该有一个带有脚本进程 ID 的保护文件。在启动时,您应该检查以确保文件不存在,或者如果确实存在,则文件中的进程 ID 不存在(通过 kill(pid, 0) 调用)。如果满足这些条件,请使用脚本的 PID 创建一个新文件,并在完成后删除该文件。
这是许多守护程序用来确保它尚未运行的相同技巧。如果守护进程突然被杀死,文件仍然存在,但其中的进程的 PID 不太可能在运行。
【讨论】:
【参考方案7】:根据您的脚本的作用,如果您取消时间限制,可能会导致问题。例如,如果您正在轮询在作业运行时没有响应的外部服务器,并且您的 cron 需要 2 小时而不是 30 分钟才能完成,那么即使之前的进程没有,您也可能会启动一堆 PHP 进程'还没有完成。这可能会导致系统不稳定和崩溃。
你可能有两种选择:
确保事先没有运行脚本的其他实例,否则在启动时退出()。 考虑将您的 cronjob 更改为守护程序。【讨论】:
【参考方案8】:它必须像发条一样每小时运行一次吗?
如果不拆分工作(您提到这不仅仅是一项简单的任务),每小时完成每项任务吗?
或者按用户拆分,按小时做 A-M,然后是 N-Z?
【讨论】:
以上是关于需要很长执行时间的 php 脚本的 Cron 作业的主要内容,如果未能解决你的问题,请参考以下文章