【问题标题】:Time Rescheduling Logic时间重排逻辑
【发布时间】:2021-04-13 14:47:06
【问题描述】:

我正在编写类似调度程序的代码(如果重要的话,在 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 中的 Event Scheduler 确实以某种我想要的方式重新安排作业。

【问题讨论】:

  • 您可以将分钟减少到最接近的小时...在重新设置时间表之前

标签: php datetime time scheduler


【解决方案1】:

我实际上找到了一种非常简单的方法来做我需要的事情,因此将其发布为答案。
如果我们有一个以秒为单位的frequency 值(至少在我的情况下),并且我们有原始的nextrun,这是最初应该运行任务的时间,那么逻辑如下:

  1. 我们需要获取当前时间(time()UTC_TIMESTAMP() 或其他)。
  2. 我们需要将当前时间与nextrun 进行比较,并以秒为单位得出它们之间的差异。
  3. 然后我们通过将时间差除以frequency 来计算在这些秒数内可以完成多少次任务迭代。
  4. 我们将结果值四舍五入 (ceil())。如果我们的值小于 1,我们可能需要对其进行清理。
  5. 我们将这个四舍五入的值乘以 frequency,这将得到与第 2 步不同的结果,这是该方法的盐分。
  6. 我们将生成的秒数添加到nextrun

就是这样。这并不能保证任务不会运行两次,如果它在步骤 6 的时间值之前几秒钟结束,但据我所知,MS Event Scheduler 具有相同的“缺陷”。
由于我在 SQL 中进行此计算,以下是在 SQL 中的样子(至少对于 MySQL/MariaDB):

UPDATE `cron__schedule` SET `nextrun`=TIMESTAMPADD(SECOND, IF(CEIL(TIMESTAMPDIFF(SECOND, `nextrun`, UTC_TIMESTAMP())/`frequency`) > 0, CEIL(TIMESTAMPDIFF(SECOND, `nextrun`, UTC_TIMESTAMP())/`frequency`), 1)*`frequency`, `nextrun`)

参照上面的逻辑来解释:

  1. UTC_TIMESTAMP()
  2. TIMESTAMPDIFF(SECOND, `nextrun`, UTC_TIMESTAMP()) - 以秒为单位的时间比较。
  3. TIMESTAMPDIFF(...)/`frequency`
  4. CEIL(...) 将值四舍五入。 IF(...) 用于消毒,因为我们可以得到 0 秒,这将导致我们根本不改变时间。
  5. CEIL(...)*`frequency`
  6. TIMESTAMPADD(...)

我不喜欢因为IF(...) 而不得不使用TIMESTAMPDIFF(...) 两次,但我不知道如何避免这种情况而不转移到存储过程,这感觉有点矫枉过正。此外,据我所知,MySQL 应该只计算一次这个值。但是,如果有人可以建议我采用更清洁的方法,我会更新答案。

【讨论】:

    【解决方案2】:

    在这种情况下没有对错之分,这实际上取决于您的业务逻辑以及您希望如何构建它。

    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。

    在此组合中加入一些异常处理和可能的数据库事务,以确保任务不会失败但仍会重新安排。

    【讨论】:

    • 在某种意义上,我正在编码的是一个数据库存储的 CRON。我有一个任务表(函数),允许向它们发送参数。它们被安排在一个单独的表中,schedule 值以秒为单位,对应于任务需要运行的频率。初始nextrun 时间在注册时设置(默认为当前时间)。一个单独的作业(可以是一个普通的 CRON 作业)获取时间
    • 这意味着,任务不能提前运行,但客观地说,它们可以运行得比计划的晚。由于我不仅允许每小时一次,而且甚至允许“每隔几分钟”或“每隔几秒钟”,因此仅删除分钟不是一种选择,除非我根据为作业设置的频率分支出逻辑。此外,我不确定我是否理解从 01:15 减少分钟数会变成 02:00 这是一项正在进行的工作,但也许这可以帮助了解我的目标:github.com/Simbiat/Cron跨度>
    • 对于最后一部分,我是说具有两个基于时间的约束的任务,每小时和每小时,可以通过获取当前时钟时间,增加 60 分钟然后减少分钟来解决部分。因此,对于在 01:15 运行的 01:00 任务,您添加 60 分钟并得到 02:15,然后删除分钟得到 02:00。
    • 我明白了。那会奏效。但是,例如,如果任务是每 2 分钟运行一次呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2013-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多