web-dev-qa-db-ja.com

Django将来的にタスクを実行する可能性があります)

モデル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は現在の日付から数か月先になる可能性があることに注意してください。

私はこれを行う2つの基本的な方法について考えました。

  1. 定期的なcronジョブ(たとえば、5分ごとなど)を使用して、過去5分以内にイベントが経過したかどうかを確認し、メソッドを実行します。

  2. celeryを使用し、onEventElapsedパラメーターを使用してetaをスケジュールして、将来的に実行します(モデルsaveメソッド内で)。

オプション1を考慮すると、潜在的な解決策は_Django-celery-beat_になります。ただし、通知を送信するために一定の間隔でタスクを実行するのは少し奇妙に思えます。さらに、私は(おそらく)それほどエレガントではない解決策になる(潜在的な)問題を思いつきました。

  • 5分ごとに、直前の5分間に経過したイベントを確認しますか?不安定なようですが、いくつかのイベントが見落とされている可能性があります(または、通知が2回送信されますか?)。潜在的な作業:通知が送信されたらTrueに設定されるモデルにブールフィールドを追加します。

次に、オプション2にも問題があります。

  • イベント開始/終了日時が移動した場合は手動で対処してください。 celeryを使用する場合、taskID(簡単、ofc)を保存し、日付が変更されたらタスクを取り消して、新しいタスクを発行する必要があります。しかし、私は読んだことがあります。セロリには、将来実行されるタスクを扱うときに(デザイン固有の)問題があります githubの未解決の問題 。これがどのように起こり、なぜそれが解決するのが簡単ではないのかがわかります。

今、私は潜在的に私の問題を解決できるいくつかのライブラリに出会いました:

  • celery_longterm_scheduler (しかし、これは、Schedulerクラスが異なるため、以前のようにセロリを使用できないことを意味しますか?これは、_Django-celery-beat_...の可能な使用法にもつながります... 2つのフレームワークでも、ジョブをキューに入れることはまだ可能ですか(実行時間が少し長くなりますが、数か月先にはありませんか?)
  • Django-apschedulerapschedulerを使用します。しかし、遠い将来に実行されるタスクを処理する方法についての情報は見つかりませんでした。

私がこれに取り組む方法に根本的な欠陥はありますか?あなたが持っているかもしれないどんな入力についても嬉しいです。

通知:これは何らかの意見に基づく可能性が高いことを知っていますが、醜いまたはエレガントなものと見なされるものに関係なく、私が見逃した非常に基本的なものがあるかもしれません。

10
Hafnernuss

私は働いている会社でこのようなことをやっていて、解決策は非常に簡単です。

通知を送信する必要があるかどうかを確認するために1時間ごとに実行されるcron /セロリビートを用意します。次に、それらの通知を送信し、完了としてマークします。これにより、通知時間が数年先になっていても送信されます。 ETAを使用することは、非常に長い待機時間を費やす方法ではありません。キャッシュ/ amqpがデータを失う可能性があります。

必要に応じて間隔を短くできますが、重複しないようにしてください。

1時間の時間差が大きすぎる場合は、1時間ごとにスケジューラを実行してください。ロジックは次のようなものになります

  1. 次の1時間に送信する必要のあるすべての通知を受け取るタスク(このスケジューラータスクを呼び出します)を毎時間実行します(セロリビート経由)-
  2. Apply_async(eta)を介してこれらの通知をスケジュールします-これは実際の送信になります

その方法論を使用すると、最高の世界(etaとbeat)の両方が得られます

2
ibaguio