Posted on 2017-07-02 | In python |[](https://yunsonbai.top/2017/07...python
采用mysql链接池,实现链接复用,解决gunicorn+gevent+django数据库高链接数问题mysql
原文链接nginx
以前分享了一篇如何提升django的并发能力文章,文章的最后结论是采用gunicorn+gthread+django的方式来提升并发能力,该方法简单的说是利用的多线程。
文章也抛出了一个问题:gunicorn+gevent+django+CONN_MAX_AGE会致使数据库链接数飙升,直至占满。若是必定要利用协程的方式启动,该怎么解决这个问题呢?看了一下django源码,找到了问题的根源,写了一下解决办法,下边分享一下。golang
仍是利用上一篇文章如何提升django的并发能力的数据模型,此次以get一条数据为例,因为某些缘由(好吧手里没有资源),采用了配置稍低的机器:sql
压测命令:docker
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'ce',
'USER': 'root',
'PASSWORD': '',
'HOST': '192.168.96.95',
'PORT': '3306',
'CONN_MAX_AGE': 600,
}
}数据库
# django/db/backends/mysql/base.py
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'mysql'
.
. django
def get_new_connection(self, conn_params):
c = Database.connect(**conn_params)
print(id(c)) # 好吧我刻意打印了一下这个id, 每次查询都会从新创建链接用新链接操做
return c 服务器
还有一处诡异的代码多线程
class BaseDatabaseWrapper:
"""Represent a database connection."""
# Mapping of Field objects to their column types.
data_types = {}
.
.
def _close(self):
if self.connection is not None:
with self.wrap_database_errors:
print('foo close') # 每次查询完又要调用close
return self.connection.close()
通过上边的代码,django关于mysql的部分没有使用链接池,致使每次数据库操做都要新建新的链接。更让我有些蒙的是,按照django的文档CONN_MAX_AGE是为了复用链接,可是为何每次都要新建链接呢?。并且最难受的是一旦咱们设置了CONN_MAX_AGE,链接并不会被close掉,而是一直在那占着。
也许是我使用的问题,出现了这个问题。无论如何,最后想了解决办法,请往下看
try:
import MySQLdb as Database
except ImportError as err:
raise ImproperlyConfigured(
'Error loading MySQLdb module.\n'
'Did you install mysqlclient?'
) from err
from django.db.backends.mysql.base import *
from django.db.backends.mysql.base import DatabaseWrapper as _DatabaseWrapper
class DatabaseWrapper(_DatabaseWrapper):
def get_new_connection(self, conn_params):
return ConnectPool.instance(conn_params).get_connection()
def _close(self):
return None # 假关闭
class ConnectPool(object):
def __init__(self, conn_params):
self.conn_params = conn_params
self.n = 5
self.connects = []
# 实现单例,实现链接池
@staticmethod
def instance(conn_params):
if not hasattr(ConnectPool, '_instance'):
ConnectPool._instance = ConnectPool(conn_params)
return ConnectPool._instance
def get_connection(self):
c = None
if len(self.connects) <= self.n:
c = Database.connect(**self.conn_params)
self.connects.append(c)
if c:
return c
index = random.randint(0, self.n)
try:
self.connects[index].ping()
except Exception as e:
self.connects[index] = Database.connect(**self.conn_params)
return self.connects[index]
利用链接池+假关闭的方式解决太高链接数的问题,若是有更好的建议,能够讨论。