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(),所以它是线程分离的。
但我认为异步工作线程异步处理请求并且位于一个线程内,所以我不知道为什么异步工作线程会导致此问题。
任何建议都会有所帮助。
它是这样工作的:
第一次创建连接时,同步工作线程将数据库连接(针对每个工作进程)存储在全局中。然后,对该进程的后续请求将使用相同的连接,直到超时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 后。
| 归档时间: |
|
| 查看次数: |
1675 次 |
| 最近记录: |