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

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

内容简介:前段时间分享了一篇文章也抛出了一个问题: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

总结

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


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

查看所有标签

猜你喜欢:

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

全景探秘游戏设计艺术

全景探秘游戏设计艺术

Jesse Schell / 吕阳、蒋韬、唐文 / 电子工业出版社 / 2010-6 / 69.00元

撬开你脑子里的那些困惑,让你重新认识游戏设计的真谛,人人都可以成为成功的游戏设计者!从更多的角度去审视你的游戏,从不完美的想法中跳脱出来,从枯燥的游戏设计理论中发现理论也可以这样好玩。本书主要内容包括:游戏的体验、构成游戏的元素、元素支撑的主题、游戏的改进、游戏机制、游戏中的角色、游戏设计团队、如何开发好的游戏、如何推销游戏、设计者的责任等。 本书适合任何游戏设计平台的游戏设计从业人员或即将......一起来看看 《全景探秘游戏设计艺术》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试