Python 芹菜自动重新加载任何更改
当Python 芹菜自动重新加载任何更改,python,celery,django-celery,Python,Celery,Django Celery,当settings.py中的cellery\u IMPORTS模块发生更改时,我可以让芹菜自动重新加载 我试图给母模块提供检测更改的功能,即使在子模块上也是如此,但它没有检测到子模块中的更改。这让我明白了检测不是由芹菜递归完成的。我在文档中搜索了它,但没有收到任何关于我的问题的回复 将项目中与芹菜相关的所有内容都添加到芹菜导入中,以检测更改,这让我非常困扰 有没有办法告诉芹菜“当项目的任何地方发生任何变化时,自动重新加载自己” 谢谢大家! 您可以使用-I |--include手动包含其他模块。将
settings.py
中的cellery\u 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
然后:
此行将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\u添加到您的\u 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/.$//')
OrangeTux的解决方案不适合我,所以我编写了一个小Python脚本来实现大致相同的效果。它使用inotify监视文件更改,如果在“修改”中检测到
、在“属性”中检测到、或在“删除”中检测到,则会触发芹菜重启
#!/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()
您可能应该更改Cellery\u CMD
,或任何其他全局变量。Cellery--autoreload
不起作用
因为您使用的是django,所以可以为此编写一个管理命令。
Django具有autoreload实用程序,runserver使用该实用程序在代码更改时重新启动WSGI服务器
同样的功能也可用于重新装载芹菜工人。创建一个名为芹菜的单独管理命令。编写一个函数以终止现有工作进程并启动新工作进程。现在将此函数挂接到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...')
# For Django>=2.2
autoreload.run_with_reloader(restart_celery)
# For django<2.1
# autoreload.main(restart_celery)
导入shlex
导入子流程
从django.core.management.base导入BaseCommand
从django.utils导入自动加载
def restart_芹菜():
cmd='pkill芹菜'
子进程调用(shlex.split(cmd))
cmd='芹菜工人-l信息-A foo'
子进程调用(shlex.split(cmd))
类命令(BaseCommand):
def句柄(自身、*参数、**选项):
打印('正在启动芹菜工人与autoreload…')
#对于Django>=2.2
自动加载。使用重新加载程序运行(重新启动芹菜)
#对于django这是我在django工作的方式:
#worker_dev.py(放在manage.py旁边)
从django.utils导入自动加载
def run_芹菜():
从projectname导入芹菜应用程序
芹菜应用程序工人主([“-Aprojectname”、“-linfo”、“-Psolo”])
打印(“使用autoreload启动芹菜工人…”)
自动加载。使用加载程序运行(运行芹菜)
然后运行python worker\u dev.py
。这具有在docker容器内工作的优点。您可以使用watchmedo
pip install watchdog
watchmedo auto-restart --directory=./ --pattern=*.py --recursive -- celery worker --app=worker.app --concurrency=1 --loglevel=INFO
通过watchmedo间接启动芹菜工人
pip install watchdog
watchmedo auto-restart --directory=./ --pattern=*.py --recursive -- celery worker --app=worker.app --concurrency=1 --loglevel=INFO
您可能希望使用gsub(“/”,”)代码>而不是sub(“/”,”)为了替换文件名中的所有斜杠,请在awk部分中使用code>?如果您的模块之一包含*py
(即任何后跟py
)的字符,则这三个字符将从模块名中删除(在我的情况下,将“copy_thing”更改为“c_thing”)。要修复它,请将sub(“.py”,”)
更新为sub(\\.py$”)
。似乎从4.0版开始,--autoreload
功能:(在新芹菜中,--autoreload
选项已被弃用,不再有效。最好是发送广播消息关闭代理,并在顶部放置一些东西,如supervisord
自动重新启动代理。我在生产中使用该选项,远程代理从web应用程序下载包启动时自动加载。这将引发异常-AttributeError:module'django.utils.autoreload'没有属性'main'
如果您使用的是django 2.2+,autoreload.main()
已被删除。请使用autoreload.run\u并重新加载(restart\u芹菜)
相反。如何只运行一个芹菜worker?此示例运行两个实例。此代码不会真正为我重新加载worker,因为它只跟踪由命令而不是worker加载的python文件。它应该跟踪属于django项目的所有文件,因为自动重新加载由django处理。如果它不是django的一部分,则不会重新加载。br在Windows上可能directory=./参数应该在Windows上以不同的方式写入。我认为这与此无关,我得到了AttributeError:模块“os”没有属性“setsid”
作为错误。谢谢,这工作得很好!