带有gunicorn的django持久数据库连接未重用

Jay*_*Foo 5 django gevent gunicorn

最近我遇到了一个关于由gunicorn提供的django网站的问题,并且worker_class是gevent。

PostgreSQL数据库经常抱怨连接过多,在django的settings.py中我将数据库CONN_MAX_AGE设置为6,因此django将在6秒内重用数据库连接。但我发现 django 没有重用它们。

经过一番挖掘,我发现有人说gunicorn的异步工作者会导致这个问题:

Django >= 1.6 上与异步工作人员的持久数据库连接失败

不要使用 Gunicorn 在 Heroku 上托管 Django 站点

但我想这是正确的原因,但我不知道为什么。

有没有人可以从asyncworker的原理或者gunicorn的源代码来分析这个问题并解释一下?

在查看了django db模块的源代码后,我知道django将数据库连接保存在ConnectionHandler()._connections中,这是一个theading.local(),如果它没有过期,将重用它。因为theading.local(),所以它是线程分离的。

但我认为异步工作线程异步处理请求并且位于一个线程内,所以我不知道为什么异步工作线程会导致此问题。

任何建议都会有所帮助。

use*_*439 0

它是这样工作的:

  • 第一次创建连接时,同步工作线程将数据库连接(针对每个工作进程)存储在全局中。然后,对该进程的后续请求将使用相同的连接,直到超时MAX_CONN_AGE,然后创建一个新连接。

  • gthread worker 预先创建线程并将每个传入请求分配给一个空闲线程以供其处理。第一次需要数据库连接时,它会被创建并存储在该线程的本地。当另一个请求分配给该线程时,现在可以重复使用相同的内容。同样MAX_CONN_AGE会生效。

  • gevent Worker 不会预先创建线程。对于每个新请求,都会生成一个新线程(准确地说是 eventlet)。一旦请求完成,eventlet 就会终止。所以,即使Django将连接存储在本地,它也永远不会被重用。除非关闭,否则连接将“泄漏”。

现在,gunicorn 的 gevent 工作线程类可以预先创建线程并使用它们吗?答案是否定的,因为 gevent 库没有线程池的概念。这是因为创建一个新的 greenlet 几乎不需要任何资源或时间,因此拥有池的概念是毫无意义的。

确实存在一个名为 的类gevent.Threadpool,但请注意,它用于创建本机线程,而不是 greenlet。还有一个gevent.pool.Pool,但不是像 那样的池Threadpool,它只是一个gevent.pool.Group有限制的池。Groups 用于将 eventlet 分组在一起,可以等待它们作为一个集合完成。

因此,当使用异步 gevent 工作类时,最好通过单独的数据库连接池实现来处理与数据库的连接,例如sqlalchemy.pool将 MAX_CONN_AGE 设置为 0 后。