Django可能会在不久的将来运行任务


9

假设我有一个模型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")

现在,当然,关键的是onEventElapsed何时调用timezone.now() >= event.end。请记住,end距离当前日期可能要几个月。

我考虑过两种基本方法:

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

  2. 使用celery并计划onEventElapsed使用eta将来要运行的参数(在models save方法中)。

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

  • 每五分钟检查一次过去五分钟内发生的事件吗?似乎有些动摇,也许某些事件错过了(或者其他事件收到了两次发送的通知?)。潜在的工作方式:向模型添加一个布尔字段,该字段设置为True在发送通知后立即设置。

同样,选项2也有其问题:

  • 手动更改事件开始/结束日期时间时的情况。使用时celerytaskID一旦日期更改并发出新任务,就必须存储(easy,ofc)并撤消任务。但是我已经读到,当处理将来运行的任务时,芹菜存在(特定于设计的)问题:github上的Open Issue。我意识到这是如何发生的,为什么它只是解决所有问题而已。

现在,我遇到了一些可以潜在地解决我的问题的库:

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

我处理此问题的方式是否存在基本原则上的缺陷?我很高兴收到您的任何意见。

注意:我知道这很可能是基于观点的,但是,不管有人认为什么是丑陋或优雅的,我可能都错过了一个非常基本的东西。


1
我想说您的方法取决于事件发生后多久最终用户需要通知。我有一个类似的问题,即用户只需要知道第二天就错过了前一天的约会。因此,在这种情况下,我在午夜运行了一项cron作业,并且按照您的建议,有一个布尔字段来标记是否已发送通知。这是一种非常简单且计算便宜的方法。
Hayden Eastwood

1
我认为答案是关于您需要发送多少个事件。如果每天有数百个事件要发送,那么单个事件在将来有多远都没有关系:使用第一个解决方案(根据需要调整重复时间),您可以运行任务以读取更新的数据。
Dos

@HaydenEastwood人们立即收到它并不重要,但是在结束日期的2-5分钟之内应该没问题。所以您做了与我的opion 1类似的事情?
哈夫纳努斯

1
@Hafnernuss是的-我认为在数据库中使用一个简单的cron调用来确定是否发送消息将很适合您的情况。
Hayden Eastwood

1
在安排任务时(而不是使工作人员感到记忆力不足),Dramatiq使用了除Celery之外的另一种方法,并且可以在您的情况下使用,请参阅Dramatiq.io/guide.html#scheduling-messages。但是正如他们所说的那样-消息代理不是数据库-当您需要计划长期事件时,第一个解决方案会更好。因此,您可以将两者结合起来:以1天为单位将事件放入MB中,例如,到期后它们将进入DB,并通过cron发送。
frost-nzcr4

Answers:


2

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

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

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

如果一个小时的时差太大,那么您可以做的就是每小时运行一个调度程序。逻辑就像

  1. 每小时运行一个任务(让我们将此任务称为调度程序任务),该任务将获取下一小时(通过芹菜节拍)需要发送的所有通知-
  2. 通过apply_async(eta)计划这些通知-这将是实际发送

使用该方法论可以同时获得最佳世界(eta和Beat)


1
谢谢。那正是我所做的!
Hafnernuss
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.