使用RabbitMQ更新Celery中的任务

时间:2019-03-25 11:52:19

标签: django rabbitmq celery

我在django项目中使用Celery创建任务以在将来的特定时间发送电子邮件。用户可以使用notify_on datetime字段创建一个Notification实例。然后,我将notify_on的值作为eta传递。

class Notification(models.Model):
    ...
    notify_on = models.DateTimeField()


def notification_post_save(instance, *args, **kwargs):
    send_notification.apply_async((instance,), eta=instance.notify_on)

signals.post_save.connect(notification_post_save, sender=Notification)

该方法的问题在于,如果用户更改notify_on,则他将收到两个(或更多)通知,而不是一个。

问题是如何更新与特定通知相关的任务,或者以某种方式删除旧通知并创建新通知。

2 个答案:

答案 0 :(得分:2)

首先,通过使用 post_save ,我们无法获取旧数据。因此,这里我覆盖了 save() 模型的 Notification 方法。除此之外,创建一个字段来存储芹菜task_id。

from celery.task.control import revoke


class Notification(models.Model):
    ...
    notify_on = models.DateTimeField()
    celery_task_id = models.CharField(max_length=100)

    def save(self, *args, **kwargs):
        pre_notify_on = Notification.objects.get(pk=self.pk).notify_on
        super().save(*args, **kwargs)
        post_notify_on = self.notify_on
        if not self.celery_task_id:  # initial task creation
            task_object = send_notification.apply_async((self,), eta=self.notify_on)
            Notification.objects.filter(pk=self.pk).update(celery_task_id=task_object.id)
        elif pre_notify_on != post_notify_on:
            # revoke the old task
            revoke(self.celery_task_id, terminate=True)
            task_object = send_notification.apply_async((self,), eta=self.notify_on)
            Notification.objects.filter(pk=self.pk).update(celery_task_id=task_object.id)

参考

  1. enter image description here
  2. Cancel an already executing task with Celery?

答案 1 :(得分:0)

我认为不需要删除以前的任务。您只需要验证正在执行的任务是否已完成即可。为此,创建一个称为校验和的新字段,它是一个UUID字段,每次您更改notify_on时都要更新该字段。在发送电子邮件的任务中检查此校验和。

class Notification(models.Model):
    checksum = models.UUIDField(default=uuid.uuid4)
    notify_on = models.DateTimeField()

def notification_post_save(instance, *args, **kwargs):
    send_notification.apply_async((instance.id, str(instance.checksum)),eta=instance.notify_on)

signals.post_save.connect(notification_post_save, sender=Notification)


@shared_task 
def send_notification(notification_id, checksum):
    notification = Notification.objects.get(id=notification_id)
    if str(notification.checksum) != checksum:
        return False
    #send email

另外,请不要每次都在通知对象上发送信号,而是在notify_on更改时发送该信号。您也可以检查一下 Identify the changed fields in django post_save signal