Django 在遥远的未来运行任务(可能)

Haf*_*uss 10 python django celery apscheduler django-celery-beat

假设我有一个模型Event。我想在活动结束后向所有受邀用户发送通知(电子邮件、推送等)。类似的东西:

class Event(models.Model):
    start = models.DateTimeField(...)
    end = models.DateTimeField(...)
    invited = models.ManyToManyField(model=User)

    def onEventElapsed(self):
        for user in self.invited:
           my_notification_backend.sendMessage(target=user, message="Event has elapsed")

Run Code Online (Sandbox Code Playgroud)

现在,当然,关键部分是调用onEventElapsedwhen timezone.now() >= event.end。请记住,end距离当前日期可能还有几个月的时间。

我想过两种基本的方法来做到这一点:

  1. 使用定期cron作业(例如,每五分钟左右)检查过去五分钟内是否发生了任何事件并执行我的方法。

  2. 使用celery和调度onEventElapsed使用eta将来要运行的参数(在模型save方法中)。

考虑选项 1,一个潜在的解决方案可能是django-celery-beat。但是,以固定的时间间隔运行任务以发送通知似乎有点奇怪。此外,我提出了一个(潜在的)问题,它(可能)会导致一个不太优雅的解决方案:

  • 每五分钟检查一次过去五分钟内发生的事件?似乎不稳定,也许有些事件被错过了(或者其他人的通知发送了两次?)。潜在的解决方法:向模型添加一个布尔字段,该字段设置为True一旦发送通知。

再说一次,选项 2 也有它的问题:

  • 手动处理事件开始/结束日期时间移动时的情况。使用时celery,必须存储taskID(easy, ofc) 并在日期更改后撤销任务并发出新任务。但我读过,芹菜在处理将来运行的任务时有(特定于设计的)问题:github 上的 Open Issue。我意识到这是如何发生的,以及为什么要解决这一切都是微不足道的。

现在,我遇到了一些可能会解决我的问题的库:

  • celery_longterm_scheduler(但这是否意味着我不能像以前那样使用 celery,因为不同的 Scheduler 类?这也与django-celery-beat...的可能用法有关。使用这两个框架中的任何一个,是否仍然可以排队作业(只是运行时间更长但不是几个月?)
  • django-apscheduler,使用apscheduler. 但是,我无法找到有关它将如何处理在遥远的将来运行的任务的任何信息。

我处理这个问题的方式是否存在根本缺陷?我很高兴您可能有任何投入。

注意:我知道这可能是基于某种意见,但是,也许我遗漏了一个非常基本的东西,无论某些人认为什么是丑陋或优雅的。

iba*_*uio 8

我工作的公司正在做类似的事情,解决方案非常简单。

让 cron / celery 每小时运行一次,以检查是否需要发送任何通知。然后发送这些通知并将其标记为已完成。这样,即使您的通知时间提前了几年,它仍然会发送。使用 ETA 并不是等待很长时间的方法,您的缓存/amqp 可能会丢失数据。

您可以根据需要缩短间隔时间,但请确保它们不重叠。

如果一小时的时差太大,那么您可以做的是每小时运行一次调度程序。逻辑会是这样的

  1. 每小时运行一个任务(让我们称之为调度程序任务),获取下一小时内需要发送的所有通知(通过 celerybeat)-
  2. 通过 apply_async(eta) 安排这些通知 - 这将是实际发送

使用这种方法会让你获得最好的世界(eta和beat)