web-dev-qa-db-ja.com

変更があった場合のCeleryの自動リロード

CELERY_IMPORTSsettings.pyのモジュールに変更があった場合、セロリを自動的にリロードさせることができます。

子モジュールでも変更を検出するように母モジュールを提供しようとしましたが、子モジュールの変更は検出されませんでした。それは、検出がセロリによって再帰的に行われないことを私に理解させます。ドキュメントで検索しましたが、問題に対する応答がありませんでした。

プロジェクトのセロリに関連するすべての部分をCELERY_IMPORTSに追加して、変更を検出するのは本当に面倒です。

「プロジェクトのどこかに変更があった場合、自分で自動リロードする」ことをセロリに伝える方法はありますか。

ありがとうございました!

20
Ahmet DAL

_-I|--include_を使用して追加のモジュールを手動で含めることができます。これをGNU findawkなどのツールと組み合わせると、すべての_.py_ファイルを見つけて含めることができます。

_$ celery -A app worker --autoreload --include=$(find . -name "*.py" -type f | awk '{sub("\./",""); gsub("/", "."); sub(".py",""); print}' ORS=',' | sed 's/.$//')
_

それを説明しましょう:

_find . -name "*.py" -type f
_

findは、_.py_を含むすべてのファイルを再帰的に検索します。出力は次のようになります。

_./app.py
./some_package/foopy
./some_package/bar.py
_

次に:

_awk '{sub("\./",""); gsub("/", "."); sub(".py",""); print}' ORS=','
_

この行は、findの出力を入力として受け取り、_./_のすべての出現を削除します。次に、すべての_/_を_._に置き換えます。最後のsub()は、_.py_を空の文字列に置き換えます。 ORSは、すべての改行を_,_に置き換えます。この出力:

_app,some_package.foo,some_package.bar,
_

最後のコマンドsedは、最後の_,_を削除します。

したがって、実行されているコマンドは次のようになります。

_$ celery -A app worker --autoreload --include=app,some_package.foo,some_package.bar
_

ソース内にvirtualenvがある場合は、_-path .path_to_your_env -Prune -o_を追加して除外できます。

_$ celery -A app worker --autoreload --include=$(find . -path .path_to_your_env -Prune -o -name "*.py" -type f | awk '{sub("\./",""); gsub("/", "."); sub(".py",""); print}' ORS=',' | sed 's/.$//')
_
16
OrangeTux

Celery --autoreloadは機能せず、 非推奨 です。

Djangoを使用しているので、そのための管理コマンドを書くことができます。 Djangoには、コードが変更されたときにWSGIサーバーを再起動するためにrunserverによって使用される自動リロードユーティリティがあります。

同じ機能を使用して、セロリワーカーをリロードできます。セロリと呼ばれる別の管理コマンドを作成します。既存のワーカーを強制終了し、新しいワーカーを開始する関数を記述します。次に、この関数をフックして、次のように自動リロードします。

import shlex
import subprocess

from Django.core.management.base import BaseCommand
from Django.utils import autoreload


def restart_celery():
    cmd = 'pkill celery'
    subprocess.call(shlex.split(cmd))
    cmd = 'celery worker -l info -A foo'
    subprocess.call(shlex.split(cmd))


class Command(BaseCommand):

    def handle(self, *args, **options):
        print('Starting celery worker with autoreload...')

        # For Django>=2.2
        autoreload.run_with_reloader(restart_celery) 

        # For Django<2.1
        # autoreload.main(restart_celery)

これで、コードベースが変更されたときに自動リロードされるpython manage.py celeryを使用してceleryworkerを実行できます。

これは開発目的のみであり、本番環境では使用しません。私から取ったコード ここに他の答え

7
ChillarAnand

OrangeTuxのソリューションはうまくいかなかったので、少しPythonスクリプトを作成して、ほぼ同じことを実現しました。inotifyを使用してファイルの変更を監視し、検出するとセロリの再起動をトリガーします。 IN_MODIFYIN_ATTRIB、またはIN_DELETE

#!/usr/bin/env python
"""Runs a celery worker, and reloads on a file change. Run as ./run_celery [directory]. If
directory is not given, default to cwd."""
import os
import sys
import signal
import time

import multiprocessing
import subprocess
import threading

import inotify.adapters


CELERY_CMD = Tuple("celery -A amcat.amcatcelery worker -l info -Q amcat".split())
CHANGE_EVENTS = ("IN_MODIFY", "IN_ATTRIB", "IN_DELETE")
WATCH_EXTENSIONS = (".py",)

def watch_tree(stop, path, event):
    """
    @type stop: multiprocessing.Event
    @type event: multiprocessing.Event
    """
    path = os.path.abspath(path)

    for e in inotify.adapters.InotifyTree(path).event_gen():
        if stop.is_set():
            break

        if e is not None:
            _, attrs, path, filename = e

            if filename is None:
                continue

            if any(filename.endswith(ename) for ename in WATCH_EXTENSIONS):
                continue

            if any(ename in attrs for ename in CHANGE_EVENTS):
                event.set()


class Watcher(threading.Thread):
    def __init__(self, path):
        super(Watcher, self).__init__()
        self.celery = subprocess.Popen(CELERY_CMD)
        self.stop_event_wtree = multiprocessing.Event()
        self.event_triggered_wtree = multiprocessing.Event()
        self.wtree = multiprocessing.Process(target=watch_tree, args=(self.stop_event_wtree, path, self.event_triggered_wtree))
        self.wtree.start()
        self.running = True

    def run(self):
        while self.running:
            if self.event_triggered_wtree.is_set():
                self.event_triggered_wtree.clear()
                self.restart_celery()
            time.sleep(1)

    def join(self, timeout=None):
        self.running = False
        self.stop_event_wtree.set()
        self.celery.terminate()
        self.wtree.join()
        self.celery.wait()
        super(Watcher, self).join(timeout=timeout)

    def restart_celery(self):
        self.celery.terminate()
        self.celery.wait()
        self.celery = subprocess.Popen(CELERY_CMD)


if __name__ == '__main__':
    watcher = Watcher(sys.argv[1] if len(sys.argv) > 1 else ".")
    watcher.start()

    signal.signal(signal.SIGINT, lambda signal, frame: watcher.join())
    signal.pause()

おそらく、CELERY_CMDまたはその他のグローバル変数を変更する必要があります。

2
Martijn