"""
OpenGauss 连接池管理器
提供高效的数据库连接池管理、连接健康检查和性能监控
"""
import threading
import time
import logging
from queue import Queue, Empty, Full
from contextlib import contextmanager
from django.db import connection
from django.conf import settings
import psycopg2
from psycopg2 import pool
logger = logging.getLogger('django.db.backends.opengauss')
class OpenGaussConnectionPool:
"""
OpenGauss 连接池管理器
提供连接复用、健康检查、自动重连等功能
"""
def __init__(self, database_settings, min_conn=1, max_conn=20,
max_idle_time=300, health_check_interval=60):
"""
初始化连接池
Args:
database_settings: Django 数据库配置
min_conn: 最小连接数
max_conn: 最大连接数
max_idle_time: 最大空闲时间(秒)
health_check_interval: 健康检查间隔(秒)
"""
self.database_settings = database_settings
self.min_conn = min_conn
self.max_conn = max_conn
self.max_idle_time = max_idle_time
self.health_check_interval = health_check_interval
self._pool = None
self._lock = threading.RLock()
self._connection_count = 0
self._created_connections = 0
self._closed_connections = 0
self._health_check_failures = 0
self.stats = {
'total_requests': 0,
'active_connections': 0,
'idle_connections': 0,
'failed_connections': 0,
'average_response_time': 0.0,
'last_health_check': None,
}
self._health_check_thread = None
self._stop_health_check = threading.Event()
self._initialize_pool()
def _initialize_pool(self):
"""初始化连接池"""
try:
conn_params = self._build_connection_params()
self._pool = psycopg2.pool.ThreadedConnectionPool(
minconn=self.min_conn,
maxconn=self.max_conn,
**conn_params
)
logger.info(f"OpenGauss 连接池初始化成功: min={self.min_conn}, max={self.max_conn}")
self._start_health_check()
except Exception as e:
logger.error(f"连接池初始化失败: {e}")
raise
def _build_connection_params(self):
"""构建连接参数"""
params = {
'host': self.database_settings.get('HOST', 'localhost'),
'port': self.database_settings.get('PORT', 5432),
'database': self.database_settings.get('NAME'),
'user': self.database_settings.get('USER'),
'password': self.database_settings.get('PASSWORD'),
}
options = self.database_settings.get('OPTIONS', {})
for key, value in options.items():
if key in ['sslmode', 'connect_timeout', 'application_name']:
params[key] = value
params.setdefault('application_name', 'Django-OpenGauss-Pool')
params.setdefault('connect_timeout', 10)
return params
@contextmanager
def get_connection(self):
"""
获取数据库连接的上下文管理器
"""
conn = None
start_time = time.time()
try:
self.stats['total_requests'] += 1
with self._lock:
if self._pool is None:
raise RuntimeError("连接池未初始化或已关闭")
conn = self._pool.getconn()
self._connection_count += 1
self.stats['active_connections'] += 1
if not self._is_connection_healthy(conn):
self._pool.putconn(conn, close=True)
conn = self._pool.getconn()
logger.debug(f"获取连接成功, 当前活跃连接数: {self.stats['active_connections']}")
yield conn
except Exception as e:
self.stats['failed_connections'] += 1
logger.error(f"连接获取失败: {e}")
if conn:
try:
self._pool.putconn(conn, close=True)
except Exception:
pass
conn = None
raise
finally:
if conn:
try:
with self._lock:
self._pool.putconn(conn)
self._connection_count -= 1
self.stats['active_connections'] -= 1
except Exception as e:
logger.error(f"连接归还失败: {e}")
response_time = time.time() - start_time
self.stats['average_response_time'] = (
(self.stats['average_response_time'] * (self.stats['total_requests'] - 1) + response_time) /
self.stats['total_requests']
)
def _is_connection_healthy(self, conn):
"""检查连接健康性"""
try:
with conn.cursor() as cursor:
cursor.execute("SELECT 1")
cursor.fetchone()
return True
except Exception:
return False
def _start_health_check(self):
"""启动健康检查线程"""
if self._health_check_thread and self._health_check_thread.is_alive():
return
self._stop_health_check.clear()
self._health_check_thread = threading.Thread(
target=self._health_check_worker,
daemon=True,
name='OpenGauss-HealthCheck'
)
self._health_check_thread.start()
logger.info("健康检查线程已启动")
def _health_check_worker(self):
"""健康检查工作线程"""
while not self._stop_health_check.wait(self.health_check_interval):
try:
self._perform_health_check()
except Exception as e:
logger.error(f"健康检查异常: {e}")
def _perform_health_check(self):
"""执行健康检查"""
start_time = time.time()
try:
with self.get_connection() as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT version(), current_timestamp")
result = cursor.fetchone()
logger.debug(f"健康检查通过, 数据库版本: {result[0]}")
self.stats['last_health_check'] = time.time()
except Exception as e:
self._health_check_failures += 1
logger.warning(f"健康检查失败 (第{self._health_check_failures}次): {e}")
if self._health_check_failures >= 3:
logger.error("连续健康检查失败,尝试重建连接池")
self._rebuild_pool()
with self._lock:
if self._pool:
self.stats['idle_connections'] = max(0, self.min_conn - self.stats['active_connections'])
def _rebuild_pool(self):
"""重建连接池"""
try:
with self._lock:
if self._pool:
self._pool.closeall()
self._initialize_pool()
self._health_check_failures = 0
logger.info("连接池重建成功")
except Exception as e:
logger.error(f"连接池重建失败: {e}")
def get_pool_stats(self):
"""获取连接池统计信息"""
with self._lock:
return {
**self.stats,
'total_created_connections': self._created_connections,
'total_closed_connections': self._closed_connections,
'current_connection_count': self._connection_count,
'health_check_failures': self._health_check_failures,
'pool_size_min': self.min_conn,
'pool_size_max': self.max_conn,
}
def close(self):
"""关闭连接池"""
logger.info("开始关闭连接池...")
if self._health_check_thread:
self._stop_health_check.set()
self._health_check_thread.join(timeout=5)
with self._lock:
if self._pool:
self._pool.closeall()
self._pool = None
logger.info("连接池已关闭")
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
class PooledDatabaseWrapper:
"""
使用连接池的数据库包装器
集成到 Django 数据库后端中
"""
_pool = None
_pool_lock = threading.RLock()
@classmethod
def get_pool(cls, database_settings):
"""获取或创建连接池"""
with cls._pool_lock:
if cls._pool is None:
pool_options = database_settings.get('OPTIONS', {}).get('CONNECTION_POOL', {})
cls._pool = OpenGaussConnectionPool(
database_settings=database_settings,
min_conn=pool_options.get('MIN_CONN', 1),
max_conn=pool_options.get('MAX_CONN', 20),
max_idle_time=pool_options.get('MAX_IDLE_TIME', 300),
health_check_interval=pool_options.get('HEALTH_CHECK_INTERVAL', 60),
)
logger.info("全局连接池已创建")
return cls._pool
@classmethod
def close_pool(cls):
"""关闭全局连接池"""
with cls._pool_lock:
if cls._pool:
cls._pool.close()
cls._pool = None
def get_pooled_connection(database_settings):
"""
获取池化连接的便捷函数
可在 Django 数据库后端中使用
"""
pool = PooledDatabaseWrapper.get_pool(database_settings)
return pool.get_connection()
class Command:
"""
Django 管理命令:查看连接池状态
python manage.py pool_status
"""
help = '显示 OpenGauss 连接池状态'
def handle(self, *args, **options):
from django.conf import settings
db_settings = settings.DATABASES['default']
try:
pool = PooledDatabaseWrapper.get_pool(db_settings)
stats = pool.get_pool_stats()
print("=== OpenGauss 连接池状态 ===")
print(f"总请求数: {stats['total_requests']}")
print(f"活跃连接数: {stats['active_connections']}")
print(f"空闲连接数: {stats['idle_connections']}")
print(f"失败连接数: {stats['failed_connections']}")
print(f"平均响应时间: {stats['average_response_time']:.3f}秒")
print(f"连接池大小: {stats['pool_size_min']}-{stats['pool_size_max']}")
print(f"健康检查失败次数: {stats['health_check_failures']}")
if stats['last_health_check']:
last_check = time.time() - stats['last_health_check']
print(f"上次健康检查: {last_check:.1f}秒前")
except Exception as e:
print(f"获取连接池状态失败: {e}")
def with_pooled_connection(func):
"""
装饰器:为函数提供池化连接
"""
def wrapper(*args, **kwargs):
from django.conf import settings
db_settings = settings.DATABASES['default']
pool = PooledDatabaseWrapper.get_pool(db_settings)
with pool.get_connection() as conn:
return func(conn, *args, **kwargs)
return wrapper
@with_pooled_connection
def execute_query(conn, sql, params=None):
"""
使用连接池执行查询的示例函数
"""
with conn.cursor() as cursor:
cursor.execute(sql, params or [])
return cursor.fetchall()
import atexit
atexit.register(PooledDatabaseWrapper.close_pool)