web-dev-qa-db-ja.com

定期的なタスクをCeleryに動的に追加/削除する方法(celerybeat)

次のように定義された関数がある場合:

def add(x,y):
  return x+y

この関数をセロリのPeriodicTaskとして動的に追加し、実行時に開始する方法はありますか? (擬似コード)のようなことをできるようにしたい:

some_unique_task_id = celery.beat.schedule_task(add, run_every=crontab(minute="*/30"))
celery.beat.start(some_unique_task_id)

また、(擬似コード)のようなものでそのタスクを動的に停止または削除したいと思います。

celery.beat.remove_task(some_unique_task_id)

または

celery.beat.stop(some_unique_task_id)

参考までに、djceleryを使用していません。djceleryを使用すると、Django= admin。

41
Jamie Forrest

いいえ、申し訳ありませんが、これは通常のセロリビートでは不可能です。

しかし、あなたがやりたいことを簡単に拡張できます。 Django-celeryスケジューラは、サブクラスがデータベースに対してスケジュールを読み書きするだけです(いくつかの最適化が行われています)。

また、Django以外のプロジェクトでもDjango-celeryスケジューラを使用できます。

このようなもの:

  • インストールDjango + Django-celery:

    $ pip install -U Django django-celery

  • Celeryconfigに次の設定を追加します。

    DATABASES = {
        'default': {
            'NAME': 'celerybeat.db',
            'ENGINE': 'Django.db.backends.sqlite3',
        },
    }
    INSTALLED_APPS = ('djcelery', )
    
  • データベーステーブルを作成します。

    $ PYTHONPATH=. Django-admin.py syncdb --settings=celeryconfig
    
  • データベーススケジューラでcelerybeatを開始します。

    $ PYTHONPATH=. Django-admin.py celerybeat --settings=celeryconfig \
        -S djcelery.schedulers.DatabaseScheduler
    

また、Django以外のプロジェクトでcelerycamとDjango Admin webserverを同じプロセスで開始するために使用できるdjcelerymonコマンドがあります。これを使用して定期的に編集することもできますNice Webインターフェースのタスク:

   $ djcelerymon

(何らかの理由で、Ctrl + Cを使用してdjcelerymonを停止できないことに注意してください。Ctrl+ Z +%1を強制終了する必要があります)

19
asksol

この質問に対する回答は google groups です。

私は著者ではありません、すべてのクレジットはジャン・マークに帰属します

これが適切な解決策です。動作確認済み、私のシナリオでは、必要に応じて他のフィールドをモデルに追加でき、「終了」メソッドを追加できるため、定期タスクをサブクラス化し、そこからモデルを作成しました。定期タスクのenabledプロパティをFalseに設定し、削除する前に保存する必要があります。サブクラス化全体は必須ではなく、schedule_everyメソッドが実際に作業を行います。タスクを終了する準備ができたら(サブクラスを作成しなかった場合)、PeriodicTask.objects.filter(name = ...)を使用してタスクを検索し、無効にしてから削除できます。

お役に立てれば!

from djcelery.models import PeriodicTask, IntervalSchedule
from datetime import datetime

class TaskScheduler(models.Model):

    periodic_task = models.ForeignKey(PeriodicTask)

    @staticmethod
    def schedule_every(task_name, period, every, args=None, kwargs=None):
    """ schedules a task by name every "every" "period". So an example call would be:
         TaskScheduler('mycustomtask', 'seconds', 30, [1,2,3]) 
         that would schedule your custom task to run every 30 seconds with the arguments 1,2 and 3 passed to the actual task. 
    """
        permissible_periods = ['days', 'hours', 'minutes', 'seconds']
        if period not in permissible_periods:
            raise Exception('Invalid period specified')
        # create the periodic task and the interval
        ptask_name = "%s_%s" % (task_name, datetime.datetime.now()) # create some name for the period task
        interval_schedules = IntervalSchedule.objects.filter(period=period, every=every)
        if interval_schedules: # just check if interval schedules exist like that already and reuse em
            interval_schedule = interval_schedules[0]
        else: # create a brand new interval schedule
            interval_schedule = IntervalSchedule()
            interval_schedule.every = every # should check to make sure this is a positive int
            interval_schedule.period = period 
            interval_schedule.save()
        ptask = PeriodicTask(name=ptask_name, task=task_name, interval=interval_schedule)
        if args:
            ptask.args = args
        if kwargs:
            ptask.kwargs = kwargs
        ptask.save()
        return TaskScheduler.objects.create(periodic_task=ptask)

    def stop(self):
        """pauses the task"""
        ptask = self.periodic_task
        ptask.enabled = False
        ptask.save()

    def start(self):
        """starts the task"""
        ptask = self.periodic_task
        ptask.enabled = True
        ptask.save()

    def terminate(self):
        self.stop()
        ptask = self.periodic_task
        self.delete()
        ptask.delete()
37
McP

これは、最終的にセロリv4.1.0に含まれる 修正 によって可能になりました。ここで、データベースバックエンドのスケジュールエントリを変更するだけで、celery-beatは新しいスケジュールに従って動作します。

ドキュメント 漠然と記述 これがどのように機能するか。 celery-beatのデフォルトスケジューラPersistentSchedulerは、スケジュールデータベースとして shelve file を使用します。 PersistentSchedulerインスタンス内のbeat_schedule辞書への変更は、このデータベースと同期され(デフォルトでは3分ごと)、その逆も同様です。ドキュメントでは、 新しいエントリを追加する方法beat_scheduleを使用してapp.add_periodic_taskに記述しています。既存のエントリを変更するには、同じnameを持つ新しいエントリを追加するだけです。辞書からのようにエントリを削除します:del app.conf.beat_schedule['name']

外部アプリを使用してセロリのビートスケジュールを監視および変更するとします。次に、いくつかのオプションがあります。

  1. シェルフデータベースファイルをopenし、その内容を辞書のように読むことができます。変更のためにこのファイルに書き戻します。
  2. Celeryアプリの別のインスタンスを実行し、そのインスタンスを使用して、上記のようにシェルフファイルを変更できます。
  3. Django-celery-beatのカスタムスケジューラクラスを使用 を使用して、スケジュールをDjango管理データベースに保存し、そこにあるエントリにアクセスできます。
  4. celerybeat-mongo からスケジューラを使用して、MongoDBバックエンドにスケジュールを保存し、そこのエントリにアクセスできます。
4
Tristan Brown

必要なモデルを提供するDjango-celery-beatというライブラリがあります。新しい定期的なタスクを動的にロードするには、独自のスケジューラを作成する必要があります。

from Django_celery_beat.schedulers import DatabaseScheduler


class AutoUpdateScheduler(DatabaseScheduler):

    def tick(self, *args, **kwargs):
        if self.schedule_changed():
            print('resetting heap')
            self.sync()
            self._heap = None
            new_schedule = self.all_as_schedule()

            if new_schedule:
                to_add = new_schedule.keys() - self.schedule.keys()
                to_remove = self.schedule.keys() - new_schedule.keys()
                for key in to_add:
                    self.schedule[key] = new_schedule[key]
                for key in to_remove:
                    del self.schedule[key]

        super(AutoUpdateScheduler, self).tick(*args, **kwargs)

    @property
    def schedule(self):
        if not self._initial_read and not self._schedule:
            self._initial_read = True
            self._schedule = self.all_as_schedule()

        return self._schedule
4

これを確認できます flask-djcelery flaskとdjceleryを設定し、参照可能なREST APIも提供します

2
Waqas Javed