芹菜自动重新加载任何变化

时间:2014-02-09 22:53:56

标签: python celery django-celery

CELERY_IMPORTS settings.py中的模块发生变化时,我可以自动重新加载芹菜。

我试图让母模块检测甚至在子模块上的变化,但它没有检测到子模块的变化。这让我明白芹菜不能递归检测。我在文档中搜索了它,但我没有遇到任何对我的问题的回复。

我真的很困扰我将项目的芹菜部分添加到CELERY_IMPORTS以检测更改。

有没有办法告诉芹菜“当项目的任何地方发生任何变化时自动重新加载”。

谢谢!

5 个答案:

答案 0 :(得分:15)

您可以使用-I|--include手动添加其他模块。将此与findawk等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()删除会用空字符串替换.pyORS,替换所有换行符。这输出:

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_MODIFYIN_ATTRIBIN_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

More detailed

答案 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容器内部工作的优势。