Gunicorn 动态反映更改的代码

Gunicorn reflect changed code dynamically

我正在开发一个 django web 应用程序,用户可以在应用程序本身中修改某些 类 的代码,通过 UI 使用 ace editor(认为是 gitlab/github 在那里你可以在线更改代码)。但是这些 类 是 django 和 celery worker 在某些时候 运行。

保存代码更改后,由于 gunicorn,django 不会选择这些更改,但由于 celery 的过程不同,因此可以正常工作。 (运行 它在本地使用 runserver 工作正常并且 django 和 celery 都选择了更改)。

有没有办法让 gunicorn 在不重新加载整个应用程序的情况下反映包含 类 的特定目录的更改?如果需要重新加载,有没有办法在不停机的情况下一个接一个地重新加载 gunicorn 的工人?

gunicron 命令:

/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app

wsgi配置文件:

import os
import sys

from django.core.wsgi import get_wsgi_application

app_path = os.path.abspath(os.path.join(
    os.path.dirname(os.path.abspath(__file__)), os.pardir))
sys.path.append(os.path.join(app_path, 'an_application'))

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")

application = get_wsgi_application()

重新加载选项“用于开发”。没有强硬的措辞说你不应该在生产中使用它。您不应该在生产中使用它的原因是因为人们会打错字,更改一个文件,可能需要对其他文件进行其他几处更改,等等。因此,您可以使您的网站无法访问,然后您就没有可用的应用程序再次修复它。

对于开发人员来说,这没有问题,因为您可以查看 shell 中的 logs/output 并重新启动它。这就是为什么@Krzysztof 的建议是最好的。将代码更改推送到您的存储库,使其通过 CI/CD 并切换 pod。如果 CI 失败,那么 CD 不会发生,所以你很好。

当然,对于问答网站来说,这个范围太大了。

为什么不将代码保存在单独的文本文件或数据库中,相关方法可以简单地将代码作为字符串动态加载并使用 exec() 执行?

假设您有一个可以由用户编辑的函数 function1。当用户提交更改时,处理输入(将函数分开,以便您知道哪个函数有什么定义),并将它们全部单独保存,如 function1function2 等,在数据库中或作为字符串的文本文件。

你需要执行一个 function1,只需加载你保存的值并使用 exec 来执行代码。

这样,您就不需要重新加载 gunicorn,因为所有 worker 总是会在 运行 时间获取更新的函数定义!

行中的内容:


def function1_original():
    # load function definition
    f = open("function1.txt", "r")
    
    # execute the string
    exec(f.read())  # this will just load the function definition
    function1()  # this will execute the user defined function

因此用户将定义:

def function1():
    # user defined code
    # blah blah
    ...

我能够通过将 python 脚本的扩展名更改为除 .py

以外的任何内容来解决这个问题

然后我使用以下函数加载了这些文件:

from importlib import util
from immportlib.machinary import SourceFileLoader

def load_module(module_name, modele_path):
    module_path = path.join(path.dirname(__file__),  "/path/to/your/files{}.anyextension".format(module_name))
    spec = util.spec_from_loader(module_name,
                                 SourceFileLoader(module_name, module_path))
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

在这种情况下,它们不是由 Gunicorn 在 RAM 中加载的,我能够即时应用更改而无需应用 evalexec 函数。