Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 时间重调度逻辑_Php_Datetime_Time_Scheduler - Fatal编程技术网

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,这可能导致运行此作业两次。理论上,我们可以使用“最

我正在编写一个类似于调度器的代码(如果需要的话,可以使用PHP),并遇到了一件有趣的事情:重新安排周期性任务很容易,但如果由于某种原因,它的运行时间比预期的要晚,那会怎么样?
例如,假设一个作业需要每小时运行一次,它的下一次计划运行时间是
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
  • 就这样。这并不能保证,如果任务在第6步的时间值前几秒结束,您将不会运行两次,但据我所知,MS事件调度器也有相同的“缺陷”。
    由于我是在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应该只计算这个值一次。但是,如果有人能给我一个建议