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

问题描述 投票:0回答:1

最近我遇到了一个关于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(),所以它是线程分离的。

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

任何建议都会有帮助。

django gunicorn gevent
1个回答
0
投票

这就是它的工作原理:

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

    MAX_CONN_AGE
    超时,然后创建一个新连接。

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

    MAX_CONN_AGE
    就会生效。

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

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

确实存在一个名为

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

因此,当使用异步 gevent 工作类时,最好在将 MAX_CONN_AGE 设置为 0 后,通过单独的数据库连接池实现(例如

sqlalchemy.pool
)来处理与数据库的连接。

© www.soinside.com 2019 - 2024. All rights reserved.