Django 是用一个线程处理 WSGI 还是 Gunicorn 中的多个请求?

Does Django use one thread to process several requests in WSGI or Gunicorn?

如题,我想知道当运行通过WSGI或Gunicorn时,Django是否使用一个线程来处理多个请求?

我知道从不应该访问的地方访问请求是一种不好的做法,但我仍然想这样做。 (我认为有充分的理由,例如在我的自定义模板加载器中访问当前用户和站点。Django 模板加载器不提供这两个实体。)

现在我设法使用以下代码从任何地方访问用户等:

import threading

_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, "request", None)

class RequestThreadMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        _thread_locals.request = request
        response = self.get_response(request)
        return response

我的question/concern是:能保证requests/users在生产中不混吗?例如,我担心第二个用户被识别为第一个用户,因为该线程正在用于我的多个请求。或者,请求在原始请求尚未完成时被另一个请求覆盖。这可能吗,或者每个请求在 WSGI 和 Gunicorn 中都有自己的本地线程?

理论上,Django使用异步实例来处理不同的请求。即当一个客户端的请求来到I/O操作时,它会发送I/O请求(即在数据库中查找),然后存储当前状态并让另一个客户端处理它的请求。因此,您不必担心“线程安全”或“线程数据安全”问题。因为在 Django 中总是有一个主线程负责生产线程。

要检查 Django 是否真的同时运行多个线程,请在您的 Linux 机器上安装并使用 htop

It is safethreading.local 阻塞函数一起使用 (这就是他们毕竟调用阻塞的原因),但最好使用 contextvars 来隔离还有合作任务。

据我所知,Django不依赖多线程。

每个 WSGI 或 Gunicorn runner-thread 只运行一个只处理一个请求的 Django 实例。

您可以使用 htop 或更好地使用 lsof

检查

这里将 Gunicorn 视为 Web 服务器。它有主进程和工人进程。 Master进程选择空闲的worker进程来处理http请求。有 3 种类型的工人:

  • 同步(同步
  • 与线程同步(worker 为 运行ning 个线程共享内存)
  • 异步(异步

没有线程的每个 sync worker 一次处理单个请求,考虑到我们有 2 个请求,它们将由 2 个不同的 worker 处理(分开并彼此隔离 python processes) 或同一个worker,但如果它有一个以上的线程,则由一个worker顺序或并行 运行 一个没有线程的同步工作者的应用程序 运行 它如下:

gunicorn --workers=3 main:app

运行 具有同步工作者和线程的应用程序:

gunicorn --workers=3 --threads=2 --worker-class=gthread main:app 

在上面的示例中,每个工作人员一次可以处理 2 个请求。

使用异步 worker,我们将有请求并发:每个 worker(python 进程)将一次处理许多请求,但由一个进程处理。要 运行 gunicorn 和异步 worker 你应该正确设置 worker class:

gunicorn --worker-class=gevent --worker-connections=1000 --workers=3 main:app

可以完全保证,如果您选择没有线程的同步工作者,请求不会混合,而且看起来异步也是如此,对于有线程的同步工作者,您应该实现线程同步,其中多个线程可以同时写入。

看看 django-simple-history 是如何工作的(也许这个库可以满足你的需求)。您可以使用:

try:
    from asgiref.local import Local as LocalContext
except ImportError:
    from threading import local as LocalContext

然后变量LocalContext()是线程安全的。

Here就是一个例子。