Php 时间重调度逻辑
我正在编写一个类似于调度器的代码(如果需要的话,可以使用PHP),并遇到了一件有趣的事情:重新安排周期性任务很容易,但如果由于某种原因,它的运行时间比预期的要晚,那会怎么样?Php 时间重调度逻辑,php,datetime,time,scheduler,Php,Datetime,Time,Scheduler,我正在编写一个类似于调度器的代码(如果需要的话,可以使用PHP),并遇到了一件有趣的事情:重新安排周期性任务很容易,但如果由于某种原因,它的运行时间比预期的要晚,那会怎么样? 例如,假设一个作业需要每小时运行一次,它的下一次计划运行时间是13.05.2021 18:00,但它运行时间是13.05.2021 20:00。现在,正常的重新安排逻辑将采用原始计划时间并添加重复频率(在这种情况下为1小时),但这将使新时间13.05.2021 19:00,这可能导致运行此作业两次。理论上,我们可以使用“最
例如,假设一个作业需要每小时运行一次,它的下一次计划运行时间是
13.05.2021 18:00
,但它运行时间是13.05.2021 20:00
。现在,正常的重新安排逻辑将采用原始计划时间并添加重复频率(在这种情况下为1小时),但这将使新时间13.05.2021 19:00
,这可能导致运行此作业两次。理论上,我们可以使用“最后一次运行”的时间,但它可以是类似于13.05.2021 20:03
,这将使新时间13.05.2021 21:03
现在我的问题是:我们可以使用什么逻辑,以便在这种情况下,下一次是
13.05.2021 21:00
?我试过用谷歌搜索类似的东西,但什么也找不到。我确实看到了,例如,Windows中的事件调度器确实以我希望的方式重新安排了作业。在这种情况下没有对错,它实际上取决于您的业务逻辑以及您希望如何构建它
WordPress和Drupal这两个最大的CMS也面临着这个问题,归结起来就是“穷人的cron”和“系统cron”。对于“穷人的cron”,这些系统依靠有人点击网站来“唤醒”调度程序,如果一个月内没有人访问你的网站,你的任务也不会运行。相反,这两个系统都建议使用系统的cron来提高一致性,并在一定的时间间隔内“唤醒”调度器。我鼓励您也在您的系统中探索这一点
下一个问题是,如何存储您的重复?您是否(有效地)有一个包含所有可能运行时间的表?那么每小时有24个条目?还是只有一个任务具有理想的运行日期/时间?与前者相比,后者通常更易于控制,前者存储了大量重复数据
那么,任务是否会自行重新调度,调度程序是否会这样做,或者在调度程序要求任务进行下一次最佳运行时是否存在中间立场?弄清楚这一点非常重要,而且有一些细微差别
另一件要考虑的事情是,如果任务运行时间早于计划,会发生什么情况?例如,如果一个任务以01:00和01:15的速度运行,世界会崩溃吗?或者它只是次优的
通常,当我构建这些类型的系统时,我的任务符合模式(OOP中的接口)并支持“下一个运行时”。调度器从“下一次运行时间”已过期的数据存储中提取所有任务并运行它们。这样,单个任务不可能同时在01:00和02:00存在,因为它只在数据存储中存在一次,例如在01:00。如果计划程序随后在01:15唤醒,它会找到已过期的01:00任务并运行该任务,然后要求该任务进行下一次运行。任务查看时钟(如果您在分布式环境中运行,则查看调度程序提供的时间),并执行自己的逻辑来确定该时间。如果逻辑为每小时,则可以从“现在”开始添加60分钟,然后删除分钟部分,因此01:15变为02:00
在这个组合中加入一些异常处理和数据库事务,以保证任务不会失败,但仍然会被重新调度。我实际上找到了一种非常简单的方法来完成我需要的任务,因此将其作为一个答案发布。
如果我们有一个以秒为单位的
frequency
(至少在我的情况下)值,并且我们有原始的nextrun
,这是任务最初应该运行的时候,那么逻辑如下:
time()
,UTC\u TIMESTAMP()
或其他任何内容)频率
,计算在这些秒数内可以完成多少次任务迭代ceil()
)。如果值小于1,则可能需要对其进行清理频率
,这将得到与第2步不同的结果,这是该方法的关键nextrun
由于我是在SQL中进行此计算的,下面是在SQL中的情况(至少对于MySQL/MariaDB):
UPDATE`cron\u schedule`SET`nextrun`=TIMESTAMPADD(秒,IF(CEIL)(TIMESTAMPDIFF(秒,nextrun`,UTC\u TIMESTAMP())/`frequency`>0,CEIL(TIMESTAMPDIFF(秒,nextrun`,UTC\u TIMESTAMP())/`frequency`),1)*`frequency`,nextrun`)
参照上述逻辑进行解释:
UTC\u时间戳()
TIMESTAMPDIFF(秒,`nextrun`,UTC_TIMESTAMP())
-以秒为单位的时间比较TIMESTAMPDIFF(…)/`frequency`
CEIL(…)
对值进行四舍五入<代码>如果(…)用于消毒,因为我们可以得到0秒,这将导致我们根本不更改时间CEIL(…)*`frequency`
TIMESTAMPADD(…)
TIMESTAMPDIFF(…)
两次,因为IF(…)
,但我不知道如何避免这种情况,而不转移到存储过程,这感觉像是一种过度杀戮。另外,据我所知,MySQL应该只计算这个值一次。但是,如果有人能给我一个建议