CELERY_IMPORTS
のsettings.py
のモジュールに変更があった場合、セロリを自動的にリロードさせることができます。
子モジュールでも変更を検出するように母モジュールを提供しようとしましたが、子モジュールの変更は検出されませんでした。それは、検出がセロリによって再帰的に行われないことを私に理解させます。ドキュメントで検索しましたが、問題に対する応答がありませんでした。
プロジェクトのセロリに関連するすべての部分をCELERY_IMPORTS
に追加して、変更を検出するのは本当に面倒です。
「プロジェクトのどこかに変更があった場合、自分で自動リロードする」ことをセロリに伝える方法はありますか。
ありがとうございました!
_-I|--include
_を使用して追加のモジュールを手動で含めることができます。これをGNU find
やawk
などのツールと組み合わせると、すべての_.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/.$//')
_
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を実行できます。
これは開発目的のみであり、本番環境では使用しません。私から取ったコード ここに他の答え 。
OrangeTuxのソリューションはうまくいかなかったので、少しPythonスクリプトを作成して、ほぼ同じことを実現しました。inotifyを使用してファイルの変更を監視し、検出するとセロリの再起動をトリガーします。 IN_MODIFY
、IN_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
またはその他のグローバル変数を変更する必要があります。