Django запуск задач (возможно) в далеком будущем - PullRequest
9 голосов
/ 27 февраля 2020

Предположим, у меня есть модель Event. Я хочу отправить уведомление (по электронной почте, pu sh, что угодно) всем приглашенным пользователям, как только событие пройдет. Что-то вроде:

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 может быть через несколько месяцев от текущей даты.

Я думал о двух основных c способах сделать это:

  1. Использовать periodi c cron задание (скажем, каждые пять минут или около того), которое проверяет, произошли ли какие-либо события в течение последних пяти минут, и выполняет мой метод.

  2. Использование celery и запланируйте onEventElapsed, используя параметр eta для запуска в будущем (в рамках метода save).

С учетом варианта 1, потенциальное решение может быть django-celery-beat. Тем не менее, кажется немного странным запускать задачу с фиксированным интервалом для отправки уведомлений. Кроме того, у меня возникла (потенциальная) проблема, которая может (вероятно) привести к не очень элегантному решению:

  • Проверять каждые пять минут на события, которые произошли в предыдущие пять минут? кажется шатким, может быть, некоторые события пропущены (или другие получают свои уведомления дважды?). Возможное решение: добавьте логическое поле в модель, для которого установлено значение True после отправки уведомлений.

Опять же, у варианта 2 также есть свои проблемы:

  • Вручную позаботьтесь о ситуации, когда перемещается дата / время начала / окончания события. При использовании celery нужно будет сохранить taskID (easy, из c) и отозвать задачу, как только даты будут изменены, и выпустить новую задачу. Но я читал, что у сельдерея есть (design-speci c) проблемы при работе с задачами, которые будут выполняться в будущем: Open Issue на github . Я понимаю, как это происходит и почему это все, но тривиально, чтобы решить.

Теперь я столкнулся с некоторыми библиотеками, которые потенциально могут решить мою проблему:

  • celery_longterm_scheduler (Но значит ли это, что я не могу использовать сельдерей как Я хотел бы иметь раньше, из-за другого класса планировщика? Это также связано с возможным использованием django-celery-beat ... Используя любую из двух структур, все еще возможно поставить в очередь задания (которые только немного дольше выполняются, но не прошло и месяца?)
  • django -apscheduler , использует apscheduler. Однако я не смог найти никакой информации о том, как он будет обрабатывать задачи, выполняемые в далеком будущем .

Есть ли какой-то фундаментальный недостаток в том, как я к этому подхожу? Я рад любым вашим комментариям.

Примечание: я знаю, что это, вероятно, Тем не менее, некоторые из них основаны на мнении, может быть, есть кое-что из базового c, которое я пропустил, независимо от того, что некоторые считают уродливым или изящным.

1 Ответ

2 голосов
/ 01 апреля 2020

Мы делаем что-то подобное в компании, в которой я работаю, и решение довольно простое.

Имейте ритм cron / celery, который запускается каждый час, чтобы проверить, нужно ли отправлять какие-либо уведомления. Затем отправьте эти уведомления и отметьте их как выполненные. Таким образом, даже если ваше уведомление будет на несколько лет вперед, оно все равно будет отправлено. Использование ETA НЕ является способом go для очень долгого времени ожидания, ваш кеш / amqp может потерять данные.

Вы можете сократить интервал в зависимости от ваших потребностей, но убедитесь, что они не перекрываются.

Если один час слишком большой из-за разницы во времени, то вы можете запускать планировщик каждый час. Logi c будет выглядеть примерно так:

  1. - запускать задачу (давайте вызовем эту задачу планировщика) ежечасно, получая все уведомления, которые необходимо отправить в течение следующего часа (через такт сельдерея) -
  2. Запланируйте эти уведомления через apply_asyn c (eta) - это будет фактическая отправка

Используя эту методологию, вы получите оба лучших мира (eta и beat)

...