python - 在多个 uWSGI 进程中使用 Flask-SQLAlchemy

标签 python mysql flask sqlalchemy flask-sqlalchemy

我一直在与我的 Flask 应用程序中的一个持续错误作斗争:

OperationalError: (_mysql_exceptions.OperationalError) (2006, 'MySQL server has gone away')

我正在使用 mySQL 服务器实例和 Flask-SQLAlchemy 模块。我仔细检查了 mySQL 实例上连接的过期时间和 SQLAlchemy 配置中的重置时间。没有问题,连接池在 mySQL 连接到期之前重置。我得出的结论是,一定是有什么问题导致连接关闭,然后该连接的下一个用户就中断了。

我正在使用生成四个进程的 uWSGI 运行 Flask 应用程序。如果我切换到单个进程,我将无法重现错误。我猜进程是通过共享连接池互相踩踏的。我添加了以下函数,以便在 uWSGI fork 进程时运行。

from uwsgidecorators import postfork

@postfork
def reset_db_connections():
    db.engine.dispose()

在启动时工作正常,并且似乎解决了多个请求同时进入时的问题。然而,现在当一个进程被重置时,该进程的下一个请求会爆炸,并出现类似但不相同的 SQL Server has gone away 错误。这是数据库的初始设置代码

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 使用范围 session ,因此它们应该在多进程环境中提供一些保护。我需要重置 fork 上的连接池吗?我是否还应该在 fork 时检查实时 session ?

更新:

我把 fork 改成了这样:

@postfork
def reset_db_connections():
    db.session.close_all()
    db.engine.dispose()
    db.create_scoped_session()

我仍然收到 OperationalError,但它只在 fork 期间发生并且似乎不会干扰请求。堆栈跟踪不包括 fork ,但不允许我捕捉它。

Traceback (most recent call last):

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/pool.py", line 636, in _finalize_fairy fairy._reset(pool)

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/pool.py", line 774, in _reset self._reset_agent.rollback()

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1563, in rollback self._do_rollback()

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1601, in _do_rollback self.connection._rollback_impl()

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 670, in _rollback_impl self._handle_dbapi_exception(e, None, None, None, None)

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1341, in _handle_dbapi_exception exc_info

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause reraise(type(exception), exception, tb=exc_tb)

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 668, in _rollback_impl self.engine.dialect.do_rollback(self.connection)

File "/home/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py", line 2519, in do_rollback dbapi_connection.rollback()

最佳答案

你必须对 uwsgi 使用 lazy-apps=true 选项。

查看此答案:uWSGI, Flask, sqlalchemy, and postgres: SSL error: decryption failed or bad record mac

我不会使用 'lazy' 选项,因为它已被弃用

When working with multiple processes with a master process, uwsgi initializes the application in the master process and then copies the application over to each worker process. The problem is if you open a database connection when initializing your application, you then have multiple processes sharing the same connection, which causes the error above.

在某些情况下,例如使用 flask_admin 或在 app/__init__.py 中调用 Base.metadata.create_all(),您的应用确实已经与数据库建立了连接在导入时。使用 lazy-apps=false uwsgi 在导入模块后 fork ,因此连接的文件描述符被复制到 child 。使用 lazy-apps=true uwsgi fork 自身然后进行导入。这样每个子流程都有自己的连接。

uWSGI tries to (ab)use the Copy On Write semantics of the fork() call whenever possible. By default it will fork after having loaded your applications to share as much of their memory as possible. If this behavior is undesirable for some reason, use the lazy-apps option. This will instruct uWSGI to load the applications after each worker’s fork().

关于python - 在多个 uWSGI 进程中使用 Flask-SQLAlchemy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34252892/

相关文章:

python - 尝试在 GAE 中使用 Set-Cookie 添加 header 时出错

php - 通过 Mysqli 搜索时没有结果

mysql - MsSQL NEWID 是否像 MySQL Rand() 一样糟糕

python - 如何在页面上显示查询数量? (谷歌应用程序引擎)

python - 如何在 Flask 中使用 session 存储用户事件信息而不需要登录?

javascript - Ajax post 返回 undefined

python - SSL 错误不安全旧版重新协商已禁用

python - 使用切片列表索引 numpy 数组

python - 如何在 django 中自定义下拉列表表单元素?

java - Spring 数据休息: Default Timestamp Fails and ID returns 0