解决gunicorn+gevent+django数据库高连接数问题

栏目: Python · 发布时间: 5年前

内容简介:前段时间分享了一篇文章也抛出了一个问题:gunicorn+gevent+django+CONN_MAX_AGE会导致数据库连接数飙升,直至占满。如果一定要利用协程的方式启动,该怎么解决这个问题呢?看了一下django源码,找到了问题的根源,写了一下解决办法,下边分享一下。还是利用上一篇文章

原文连接

引言

前段时间分享了一篇 如何提高django的并发能力 文章,文章的最后结论是采用gunicorn+gthread+django的方式来提高并发能力,该方法简单的说是利用的多线程。

文章也抛出了一个问题:gunicorn+gevent+django+CONN_MAX_AGE会导致数据库连接数飙升,直至占满。如果一定要利用协程的方式启动,该怎么解决这个问题呢?看了一下django源码,找到了问题的根源,写了一下解决办法,下边分享一下。

说明

还是利用上一篇文章 如何提高django的并发能力 的数据模型, 这次以get一条数据为例 ,由于某些原因(好吧手里没有资源),采用了配置稍低的机器:

  • 服务器: 4核+4G (docker)
  • 压测机: 4核+2G (docker)
  • django: 2.0.8
  • msyql: 4核+4G(docker) max_connections:1000 max_user_connections:1000

压测方式及命令

重现问题

settings

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'ce',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '192.168.96.95',
        'PORT': '3306',
        'CONN_MAX_AGE': 600,
    }
}

启动及压测结果

  • 启动: gunicorn --env DJANGO_SETTINGS_MODULE=test_dj21.settings test_dj21.wsgi:application -w 8 -b 0.0.0.0:8080 -k gevent --max-requests 40960 --max-requests-jitter 5120

  • 数据库连接数展示

    解决gunicorn+gevent+django数据库高连接数问题

    数据库连接数

  • qps展示

    解决gunicorn+gevent+django数据库高连接数问题

    qps

为什么能达到1000多, 因为一直再查同一条数据。

问题分析与解决

数据库连接数为什么这么高

# django/db/backends/mysql/base.py
class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'mysql'
    .
    .

    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掉,而是一直在那占着。

也许是我使用的问题,出现了这个问题。不管如何,最后想了解决办法,请往下看

问题的解决

代码部分

  • settings代码
DATABASES = {
    'default': {
        'ENGINE': 'test_dj21.db.backends.mysql',  # 好吧核心都在这
        'NAME': 'ce',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '10.10.10.10',
        'PORT': '3306',
        'CONN_MAX_AGE': 600,
    }
}
  • test_dj21.db.backends.mysql所在位置

    解决gunicorn+gevent+django数据库高连接数问题

    tree

  • base.py
import random
from django.core.exceptions import ImproperlyConfigured

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]

压测结果

  • 数据库连接数展示

    解决gunicorn+gevent+django数据库高连接数问题

    数据库连接数

  • qps展示

    解决gunicorn+gevent+django数据库高连接数问题

    qps

总结

利用连接池+假关闭的方式解决过高连接数的问题,如果有更好的建议,可以讨论。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Algorithmic Beauty of Plants

The Algorithmic Beauty of Plants

Przemyslaw Prusinkiewicz、Aristid Lindenmayer / Springer / 1996-4-18 / USD 99.00

Now available in an affordable softcover edition, this classic in Springer's acclaimed Virtual Laboratory series is the first comprehensive account of the computer simulation of plant development. 150......一起来看看 《The Algorithmic Beauty of Plants》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具