Flask应用在mod_wsgi/Apache下闲置后重启,开发服务器无异常
Flask应用部署到Apache+mod_wsgi后闲置重启致数据库连接丢失的问题解决
问题描述
Flask应用在开发服务器上运行正常,但部署到apache2+mod_wsgi环境后,短暂闲置(甚至仅10秒)就会触发应用重启,导致数据库连接丢失。核心逻辑为:用flask_login实现用户登录,通过PostgreSQL+psycopg2验证用户名密码,验证成功后创建数据库连接并存储在Connections类的Conns全局字典中,后续操作复用该连接。闲置后应用重启,全局字典中的连接被清空,引发连接丢失问题。
代码示意
Model.py
import psycopg2 class Connections(): # 存储每个用户的PostgreSQL连接 Conns = {} def create(self, Username, Password): # 创建连接并缓存 self.Conns[Username] = psycopg2.connect(host, Username, Password, ...) def getConnection(self, Username): # 返回对应用户的连接 return self.Conns[Username] class Model(): # 派生类对应数据库表,实例存储表中一行数据 @classmethod def Init(cls): # 手动初始化方法 ... def save(self): # 执行数据库INSERT/UPDATE操作 ...
Models.py
from Model import * class Person(Model): _fields = [ idField('id'), textField('name') ] def __init__(self, id=None, get=False, Username=None): super().__init__(id=id, get=get, Username=Username) # 实例需记录用户名以获取对应数据库连接
Application.py
from flask import Flask, render_template, redirect import flask_login from Models import Person # 初始化模型 Person.Init() app = Flask(__name__) login_manager = flask_login.LoginManager() login_manager.init_app(app) @app.route("/login/") def login(): form = FormLogin() # 基于FlaskForm的登录表单 if form.validate_on_submit(): success = Connections().create(form.user_id.data, form.password.data) if success: user = User(form.user_id.data) # flask_login所需的User类 flask_login.login_user(user) return redirect(...) @app.route("/logout/") def logout(): # 关闭对应用户的数据库连接 ...
wsgi.py
from Application import app application = app
已尝试的思路
- 为每个数据库请求重新创建连接,但担心性能损耗(开发环境下该逻辑正常)
- 怀疑登录逻辑存在根本性问题(但开发环境无异常)
- 考虑调整mod_wsgi配置
- 排查其他未知问题
补充说明
- apache2的error.log中无相关报错
- 有提示称apache2/mod_wsgi会因闲置回收进程以节省资源,问题可能出在代码逻辑
- 可能需要修改代码逻辑
- 可考虑替换为开发者能控制进程启停的轻量Web服务器(如开发服务器)
解决方案与排查思路
1. 调整mod_wsgi进程回收配置
mod_wsgi默认会对闲置进程进行回收,可通过apache配置延长闲置超时时间,减少重启频率。在apache的虚拟主机配置中添加或修改以下参数:
WSGIDaemonProcess myapp user=www-data group=www-data threads=5 idle-timeout=300 WSGIProcessGroup myapp
idle-timeout=300:设置进程闲置5分钟后才回收,根据业务需求调整时长threads=5:设置每个进程的线程数,提升并发处理能力
2. 重构数据库连接管理逻辑
全局字典存储连接的方式无法应对进程重启,建议改用连接池或请求级连接管理:
方案A:使用psycopg2连接池
连接池可复用连接,避免频繁创建销毁连接的性能损耗,同时进程重启后会重新初始化连接池:
# 修改Model.py中的Connections类 from psycopg2 import pool class Connections(): _connection_pool = None @classmethod def init_pool(cls, host, dbname, user, password, minconn=1, maxconn=10): cls._connection_pool = pool.SimpleConnectionPool(minconn, maxconn, host=host, dbname=dbname, user=user, password=password) @classmethod def get_connection(cls): return cls._connection_pool.getconn() @classmethod def return_connection(cls, conn): cls._connection_pool.putconn(conn) @classmethod def close_all(cls): cls._connection_pool.closeall() # 在Application.py中初始化连接池并管理请求生命周期 from flask import g @app.before_first_request def init_db_pool(): Connections.init_pool(host='your_host', dbname='your_db', user='default_user', password='default_pass') @app.teardown_request def teardown_db(exception): conn = getattr(g, '_db_conn', None) if conn is not None: Connections.return_connection(conn) # 修改Model类获取连接的方式 class Model(): def get_db_conn(self): conn = Connections.get_connection() setattr(g, '_db_conn', conn) return conn
如果需要按用户权限使用不同数据库账号,可调整连接池初始化逻辑,比如为每个用户维护独立连接池,或在登录时验证后获取对应权限的连接。
方案B:请求级连接管理
每个请求创建连接,请求结束后关闭,虽然会有一定性能开销,但实现简单,适合中小流量场景:
# 修改Application.py,在请求前后管理连接 from flask import g @app.before_request def get_db_conn(): # 从当前登录用户获取账号密码,创建连接 if flask_login.current_user.is_authenticated: g.db_conn = psycopg2.connect(host, flask_login.current_user.username, flask_login.current_user.password, ...) @app.teardown_request def close_db_conn(exception): conn = getattr(g, 'db_conn', None) if conn is not None: conn.close()
3. 替换Web服务器
如果不想调整apache配置,可改用gunicorn+nginx组合,开发者能更灵活控制进程参数:
- 用gunicorn启动应用:
gunicorn -w 4 -t 300 Application:app-w 4:启动4个worker进程-t 300:设置worker进程闲置300秒后才回收
- 搭配nginx做反向代理,处理静态资源和请求转发
内容的提问来源于stack exchange,提问作者Ulrich Goebel
相关产品推荐
相关产品推荐

