Django 运行 任务(可能)在遥远的未来
Django run tasks (possibly) in the far future
假设我有一个模型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")
现在,当然,关键部分是每当 timezone.now() >= event.end
时调用 onEventElapsed
。
请记住,end
可能距离当前日期还有几个月。
我想过两种基本的方法:
使用周期性的 cron
作业(例如,每五分钟左右)检查过去五分钟内是否有任何事件发生并执行我的方法。
使用 celery
并使用 eta
参数安排 onEventElapsed
将来成为 运行(在模型 save
中方法).
考虑选项 1,可能的解决方案可能是 django-celery-beat
。但是,运行 以固定时间间隔发送通知的任务似乎有点奇怪。此外,我提出了一个(潜在的)问题,该问题(可能)会导致一个不太优雅的解决方案:
- 每五分钟检查一次在前五分钟内发生的事件?似乎摇摇欲坠,也许错过了一些事件(或者其他人收到了两次通知?)。潜在的解决方法:在发送通知后将布尔字段添加到设置为
True
的模型。
话又说回来,选项2也有问题:
- 手动处理事件 start/end 日期时间移动时的情况。使用
celery
时,必须存储 taskID
(简单,ofc)并在日期更改后撤销任务并发出新任务。但我读过,芹菜在处理未来 运行 的任务时存在(特定于设计的)问题:Open Issue on github。我意识到这是如何发生的,以及为什么要解决这一切都是微不足道的。
现在,我遇到了一些可能会解决我的问题的库:
- celery_longterm_scheduler(但这是否意味着我不能像以前那样使用 celery,因为调度程序 class 不同?这也与
django-celery-beat
的可能用法有关。 .. 使用这两个框架中的任何一个,是否仍然可以对作业进行排队(只是稍微长一点-运行宁但不是几个月后?)
- django-apscheduler,使用
apscheduler
。但是,我找不到任何关于它将如何处理 运行 在遥远的未来的任务的信息。
我处理这个问题的方式是否存在根本性缺陷?我很高兴收到您的任何意见。
注意:我知道这可能是基于某种观点,但是,也许有一个非常基本的东西我错过了,不管有些人认为它是丑陋的还是优雅的。
我所在的公司正在做这样的事情,解决方法也很简单。
让 cron / celery 每小时 运行 节拍一次,以检查是否需要发送任何通知。
然后发送这些通知并将它们标记为已完成。这样,即使您的通知时间提前了数年,它仍会发送。使用 ETA 不是等待很长时间的方法,您的缓存/amqp 可能会丢失数据。
您可以根据需要减少间隔时间,但请确保它们不重叠。
如果一个小时的时差太大,那么你可以做的是,运行每小时一个调度程序。逻辑类似于
- 运行 一个任务(让我们称之为调度程序任务)每小时获取所有需要在下一个小时内发送的通知(通过 celery beat)-
- 通过 apply_async(eta) 安排这些通知 - 这将是实际发送
使用该方法将使您获得最佳世界(eta 和节拍)
假设我有一个模型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")
现在,当然,关键部分是每当 timezone.now() >= event.end
时调用 onEventElapsed
。
请记住,end
可能距离当前日期还有几个月。
我想过两种基本的方法:
使用周期性的
cron
作业(例如,每五分钟左右)检查过去五分钟内是否有任何事件发生并执行我的方法。使用
celery
并使用eta
参数安排onEventElapsed
将来成为 运行(在模型save
中方法).
考虑选项 1,可能的解决方案可能是 django-celery-beat
。但是,运行 以固定时间间隔发送通知的任务似乎有点奇怪。此外,我提出了一个(潜在的)问题,该问题(可能)会导致一个不太优雅的解决方案:
- 每五分钟检查一次在前五分钟内发生的事件?似乎摇摇欲坠,也许错过了一些事件(或者其他人收到了两次通知?)。潜在的解决方法:在发送通知后将布尔字段添加到设置为
True
的模型。
话又说回来,选项2也有问题:
- 手动处理事件 start/end 日期时间移动时的情况。使用
celery
时,必须存储taskID
(简单,ofc)并在日期更改后撤销任务并发出新任务。但我读过,芹菜在处理未来 运行 的任务时存在(特定于设计的)问题:Open Issue on github。我意识到这是如何发生的,以及为什么要解决这一切都是微不足道的。
现在,我遇到了一些可能会解决我的问题的库:
- celery_longterm_scheduler(但这是否意味着我不能像以前那样使用 celery,因为调度程序 class 不同?这也与
django-celery-beat
的可能用法有关。 .. 使用这两个框架中的任何一个,是否仍然可以对作业进行排队(只是稍微长一点-运行宁但不是几个月后?) - django-apscheduler,使用
apscheduler
。但是,我找不到任何关于它将如何处理 运行 在遥远的未来的任务的信息。
我处理这个问题的方式是否存在根本性缺陷?我很高兴收到您的任何意见。
注意:我知道这可能是基于某种观点,但是,也许有一个非常基本的东西我错过了,不管有些人认为它是丑陋的还是优雅的。
我所在的公司正在做这样的事情,解决方法也很简单。
让 cron / celery 每小时 运行 节拍一次,以检查是否需要发送任何通知。 然后发送这些通知并将它们标记为已完成。这样,即使您的通知时间提前了数年,它仍会发送。使用 ETA 不是等待很长时间的方法,您的缓存/amqp 可能会丢失数据。
您可以根据需要减少间隔时间,但请确保它们不重叠。
如果一个小时的时差太大,那么你可以做的是,运行每小时一个调度程序。逻辑类似于
- 运行 一个任务(让我们称之为调度程序任务)每小时获取所有需要在下一个小时内发送的通知(通过 celery beat)-
- 通过 apply_async(eta) 安排这些通知 - 这将是实际发送
使用该方法将使您获得最佳世界(eta 和节拍)