当CELERY_IMPORTS
settings.py
中的模块发生变化时,我可以自动重新加载芹菜。
我试图让母模块检测甚至在子模块上的变化,但它没有检测到子模块的变化。这让我明白芹菜不能递归检测。我在文档中搜索了它,但我没有遇到任何对我的问题的回复。
我真的很困扰我将项目的芹菜部分添加到CELERY_IMPORTS
以检测更改。
有没有办法告诉芹菜“当项目的任何地方发生任何变化时自动重新加载”。
谢谢!
答案 0 :(得分:15)
您可以使用-I|--include
手动添加其他模块。将此与find
和awk
等GNU工具结合使用,您就可以找到所有.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/.$//')
答案 1 :(得分:6)
芹菜<search:options>
不起作用且为deprecated。
由于您使用的是django,因此您可以为此编写管理命令。 Django具有自动重载实用程序,当代码更改时,runserver将使用该实用程序重新启动WSGI服务器。
可以使用相同的功能重新加载芹菜工人。创建一个名为celery的独立管理命令。编写一个函数来杀死现有的worker并启动一个新的worker。现在将此函数挂钩到autoreload,如下所示。
--autoreload
现在你可以运行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...')
autoreload.main(restart_celery)
的芹菜工作者,它会在代码库发生变化时自动加载。
这仅用于开发目的,不在生产中使用。代码来自我的other answer here。
答案 2 :(得分:2)
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
或任何其他全局变量。
答案 3 :(得分:2)
您可以使用watchmedo
pip install watchdog
通过watchmedo间接启动芹菜工作者
watchmedo auto-restart --directory=./ --pattern=*.py --recursive -- celery worker --app=worker.app --concurrency=1 --loglevel=INFO
答案 4 :(得分:0)
这是我使其在Django中工作的方式:
# worker_dev.py (put it next to manage.py)
from django.utils import autoreload
def run_celery():
from projectname import celery_app
celery_app.worker_main(["-Aprojectname", "-linfo", "-Psolo"])
print("Starting celery worker with autoreload...")
autoreload.run_with_reloader(run_celery)
然后运行python worker_dev.py
。这具有在Docker容器内部工作的优势。