You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 12:19:51