1

我正在将我的网站移植到 python/django,其中一个主要练习涉及一组数据,用户可以在其中安排本地时间的事件,并让它每天发生。

目前我有一个 cron 作业(在另一台服务器上),它每 5 分钟执行一次方法,看看是否需要在接下来的 10 分钟内安排任何事情。

我存储一个时间值,以及每个作业的用户本地时区

做这个的最好方式是什么?

现在我正在研究一个功能:

  • 将服务器时间转换为用户本地时间。
  • 创建本地化的“今天”本地日期时间对象和用户指定的时间
  • 检查是否在用户警报响起的 10 分钟内。
  • 如果是23:50-23:59:59之间,用户设置的时间是00:00-00:10 本地化的“今天”是用“明天”的日期创建的。(例如,如果距离午夜 2 分钟,并且用户希望在 12:01 有一个事件,我会以明天的日期计算事件)
  • 我在安排时设置了 last_scheduled 字段,并设置了 last_fired 字段以确保我不会发送多个。

如果是在 10 分钟内,我会安排一个很快就会触发的任务(线程,等等)。

不太确定这里的最佳实践。我应该:
继续检查我将来是否有任何任务并安排短期任务?
提前预生成我所有的时间(可能一次一个月?)
完全做其他事情?
我也在想我总是可以安排“下一个”活动,但我担心如果说我的服务器离线,而我错过了“下一个”活动,那么第二天就永远不会安排好了。

澄清:

  • 我存储每个工作的时间和时区(例如美国/东部的中午)。
  • 我正在纠正 DST,因此在计算 UTC 时间时,我以 UTC 格式取今天的日期,转换为本地时间,然后用它来计算增量。我正在使用 pytz 和 normalize() 来确保我不会遇到任何奇怪的 DST 问题。
  • 我确实有一个最后安排和最后运行的时间,以确保我不会重复执行。

看看下面的解决方案,我想我唯一的其他观察结果是,如果出于某种原因我错过了预定的时间,我的“下一个”将永远不会发生,因为它已经过去了。我想我可以制作第二个功能来修复任何错过的警报。

编辑: 在摸索了下面的答案之后,我想出了以下不太糟糕的情况:

我有以下字段

  • 上次事件执行时间
  • 上次安排的活动
  • 下一个事件执行时间
  • 一天中的时间和时区

每当我:更新事件或触发事件时,我都会计算并设置 next_run_time。这将执行以下操作:

  • 如果它有最后一次运行时间,则计算 next_run_time,至少在未来 2 小时(通过添加一些填充来避免 DST 问题)。
  • 如果该活动从未进行过,请在未来至少安排 15 分钟(避免任何多个同时安排)

我的预定工作执行以下操作:

  1. 检查在接下来的 15 分钟内具有 next_run_time 且当前未安排的所有事件。安排任何匹配。

安排工作:

  • 安排任务,并将作业设置为“现在”

任务执行时(成功):

  • last_run_time 更新为“现在”
  • next_run_time 重新计算

如果任务失败: - 该作业将在未来 30 秒后重新安排。如果失败超过阈值(在我的情况下逾期 3 分钟),任务将中止,并重新计算第二天的 next_run_time。这会被记录下来,希望不会发生太多

这似乎很有效,因为我的活动总是(每天),所以我有能力在时间里扔一些填充物并避免一些毛茸茸的问题

4

2 回答 2

3

(我会将此作为评论,但不允许新用户使用它们)看看芹菜,也许它会帮助http://docs.celeryproject.org/en/latest/userguide/tasks.html

于 2013-06-30T01:04:30.923 回答
2

我会避开 Python/Django 的细节,因为那不是我的专业领域。但总的来说,您所描述的类型的任务调度程序应按以下方式运行(恕我直言):

  • 将调度定义与执行时间分开
  • 计划定义应在用户本地时间定义,并包括时区 ID。
  • 执行时间应以 UTC 为单位。
  • 当任务执行时,它应该从调度中计算下一次执行时间。

让我们来看一个例子。

  • 用户说,“在美国东部时间每晚午夜跑步”。
  • 我们存储“每日,00:00 America/New_York”的时间表。
  • 我们计算第一个执行时间为2013-06-30T04:00:00Z
  • 使用您喜欢的任何机制,在执行时运行作业。如果您定期轮询需要运行的作业,只需查看时间是否已过(ExecTime <= utcnow)。如果您可以依赖事件系统、cron 作业等,那可能会更好。
  • 作业运行时,使用调度计算下一次执行时间。

为什么要按当地时间安排?好吧,在东部时间的情况下,由于夏令时,它将在从 UTC 的 -5 小时到 -4 小时之间转换。如果计划严格基于 UTC,那么在 DST 转换之后,您会发现作业在用户认为错误的时间运行。

此外,您应该考虑处理故障、重试等。并且您不希望作业在每个计划执行中运行多次,因此如果您有多个作业,您可能需要一种方法将其标记为“正在进行中”程序检查任务。有时您可能需要更复杂的锁定策略来确保多个工作进程不会执行相同的任务。这有点超出了我可以在这里写的范围。

您还应该考虑如何处理由夏令时转换引起的本地时间的歧义。考虑“回退”样式转换,如果用户说要在“每晚 1:30 AM”运行,但一年中有一个晚上 1:30 发生两次,你想做什么?如果你没有做任何特别的事情,它会在第一次出现时运行——通常是白天。用户可能期望标准时间,因此您可能需要检查这一点。即使你只是在午夜跑步,你也不能免除这个决定。有几个时区在午夜时分进行转换(例如巴西)。

如果所有这些听起来工作量太大,您可能只想寻找一个已经编写好的作业调度程序。例如,Java上的Quartz或.Net 堆栈上的Quartz.Net 。我并不直接熟悉它,但搜索出现了 Python 的APScheduler,看起来非常相似。

于 2013-06-29T22:37:40.190 回答