我一直在我的Flask应用程序中遇到持久性错误:
OperationalError: (_mysql_exceptions.OperationalError) (2006, 'MySQL server has gone away')
我正在使用mySQL服务器实例和Flask-SQLAlchemy模块 . 我仔细检查了mySQL实例上的连接的到期时间以及SQLAlchemy配置中的重置时间 . 没有问题,在mySQL连接到期之前重置连接池 . 我得出结论,必须有一些问题导致连接关闭,然后该连接的下一个用户窒息 .
我正在使用生成四个进程的uWSGI来运行Flask应用程序 . 如果我切换到单个进程,我无法重现错误 . 我猜测进程是通过共享连接池相互踩踏的 . 每当uWSGI分叉进程时,我都会添加以下函数 .
from uwsgidecorators import postfork
@postfork
def reset_db_connections():
db.engine.dispose()
在启动时工作正常,并且当多个请求同时进入时似乎解决了问题 . 但是,现在当一个进程被重置时,该进程的下一个请求会以类似但不一样的方式爆炸,SQL Server已经消失了 . 这是数据库的初始设置代码
def configure_db():
from my_application.models import SomeModel
db.create_all()
db = SQLAlchemy(app, session_options={'expire_on_commit': False})
configure_db()
数据库的典型用法如下所示:
def save(self):
try:
db.session.add(self)
db.session.commit()
except Exception, ex:
app.logger.error("Error saving campaign: %s" % ex)
db.session.rollback()
读取是以下两种风格之一:
user = db.session.query(User).filter(User.email == email).scalar()
user = User.query.filter(User.email == email).scalar()
我的理解是Flask-SQLAlchemy使用范围会话,因此他们应该在多进程环境中提供一些保护 . 我需要在fork上重置连接池吗?在分叉时我是否也应该检查实时会话?
更新:
我把fork更改为:
@postfork
def reset_db_connections():
db.session.close_all()
db.engine.dispose()
db.create_scoped_session()
我仍然得到OperationalError,但它只发生在fork中,并且似乎不会干扰请求 . 堆栈跟踪不包括fork,但不允许我捕获它 .
回溯(最近一次调用最后一次):文件“/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/pool.py”,第636行,在_finalize_fairy fairy._reset(pool)文件中“ /home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/pool.py“,第774行,在_reset self._reset_agent.rollback()文件”/ home / vagrant / env / local / lib /python2.7/site-packages/sqlalchemy/engine/base.py“,第1563行,在回滚中自我._do_rollback()文件”/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy /engine/base.py“,第1601行,在_do_rollback self.connection._rollback_impl()文件”/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py“,第670行,在_rollback_impl self._handle_dbapi_exception(e,None,None,None,None)文件“/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py”,line 1341,在_handle_dbapi_exception exc_info文件“/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py”,第199行,在raise_from_cause中重新加载(类型(e) xception),exception,tb = exc_tb)文件“/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py”,第668行,在_rollback_impl self.engine.dialect中 . do_rollback(self.connection)文件“/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py”,第2519行,在do_rollback中dbapi_connection.rollback()
1 回答
你必须对uwsgi使用lazy-apps = true选项 .
看到这个答案:uWSGI, Flask, sqlalchemy, and postgres: SSL error: decryption failed or bad record mac
我不会使用'lazy'选项,因为它已被弃用
在某些情况下,例如在
app/__init__.py
中使用flask_admin或调用Base.metadata.create_all()
,您的应用确实在导入时已经与数据库 Build 了连接 . 导入模块后使用lazy-apps=false
uwsgi分叉,从而将连接的文件描述符复制到子代 . 随着lazy-apps=true
uwsgi分叉,然后进行导入 . 这样每个子流程都有自己的连接 .