python django并发访问挂掉,解决django高并发时数据库连接量过大的问题(实现连接池的方法)...
Django默認每訪問一次數(shù)據(jù)庫都會創(chuàng)建一個新的數(shù)據(jù)庫連接,執(zhí)行完數(shù)據(jù)庫操作后再關(guān)閉連接。這在高并發(fā)的場景下會導致連接數(shù)不斷增多,最終出現(xiàn)“too many connections”錯誤。
如果我們使用gunicorn的gevent模式運行項目,那么不能在項目中使用threading(多線程)模塊進行數(shù)據(jù)庫操作。因為gevent會給threading模塊動態(tài)打補丁,這會導致數(shù)據(jù)庫連接無法復用,出現(xiàn)“too many connections”錯誤。
要解決以上問題,我們需要實現(xiàn)一個數(shù)據(jù)庫的連接池。實現(xiàn)連接池的方法參考了https://yunsonbai.top/2017/07/02/gunicorn-gevent-django/,這里做了一些調(diào)優(yōu)。實現(xiàn)的步驟如下:
1. 在項目的settings.py中配置數(shù)據(jù)庫連接參數(shù),使用自定義的數(shù)據(jù)庫引擎
DATABASES = {
'default': {
'ENGINE': 'myproject.mysql', # 注意這里必須是.mysql結(jié)尾
'POOL_SIZE': 20, # 每個進程的連接池的大小,總連接數(shù)=20*總進程數(shù)
'NAME': 'test',
'USER': 'root',
'HOST': '127.0.0.1',
'PASSWORD': '******',
'STORAGE_ENGINE': 'INNODB',
'PORT': '3306',
'CHARSET': 'utf-8',
'CONN_MAX_AGE': 28790, # 比mysql默認的wait_timeout小10秒
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB',
}
}
}
2. 在項目中增加自定義的連接池模塊
以項目的根目錄為myproject為例,新建以下兩個文件:
myproject/mysql/__init__.py
myproject/mysql/base.py
這里的文件路徑mysql/base.py是固定搭配,文件夾、文件的名稱和層級關(guān)系不能變,否則會造成django及其插件導入數(shù)據(jù)庫引擎失敗,從而自動使用默認的數(shù)據(jù)庫引擎。
__init__.py是空文件,當然也可以填充其他內(nèi)容。base.py的內(nèi)容如下:
# -*- coding: utf-8 -*-
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):
pool_size = self.settings_dict.get('POOL_SIZE') or 1
return ConnectPool.instance(conn_params, pool_size).get_connection()
def _close(self):
return None # 覆蓋掉原來的close方法,查詢結(jié)束后連接不會自動關(guān)閉
class ConnectPool(object):
def __init__(self, conn_params, pool_size):
self.conn_params = conn_params
self.pool_size = pool_size
self.connects = []
# 實現(xiàn)連接池的單例
@staticmethod
def instance(conn_params, pool_size):
if not hasattr(ConnectPool, '_instance'):
ConnectPool._instance = ConnectPool(conn_params, pool_size)
return ConnectPool._instance
def get_connection(self):
if len(self.connects) < self.pool_size:
new_connect = Database.connect(**self.conn_params)
self.connects.append(new_connect)
return new_connect
index = random.randint(0, self.pool_size - 1) # 注意這里和range不一樣,要減1
try:
self.connects[index].ping()
except:
self.connects[index] = Database.connect(**self.conn_params)
return self.connects[index]
3. 調(diào)整mysql的配置
調(diào)整mysql的max_connections,使其大于項目的總連接數(shù)(連接池大小*服務(wù)進程數(shù)),修改"/etc/mysql/my.cnf",在[mysqld]配置項下設(shè)置max_connections=1000,然后重啟mysql的服務(wù)。查看配置結(jié)果的sql:
SHOW VARIABLES LIKE "max_connections";
配置settings的CONN_MAX_AGE參數(shù),使其小于數(shù)據(jù)庫的wait_timeout配置,查看wait_timeout的sql:
SHOW GLOBAL VARIABLES LIKE "%wait%";
4. 驗證結(jié)果
啟動項目后訪問幾個會與數(shù)據(jù)庫交互的頁面,然后查看連接的數(shù)量,驗證連接池的效果,查看連接數(shù)量的sql:
SHOW processlist;
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的python django并发访问挂掉,解决django高并发时数据库连接量过大的问题(实现连接池的方法)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 截取字符串的方法,php截取字符
- 下一篇: php实现tail,Linux-如何用p