Flask、SQLAlchemy 和 MySQL 服务器已经消失
Flask, SQLAlchemy and MySQL Server has gone away
我的 Flask 应用程序中有一个到 MariaDB (MySQL) 服务器的 SQLAlchemy 连接。我使用范围会话,并在应用程序上下文拆卸时删除会话。
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, pool_recycle=3600, isolation_level='READ_COMMITTED')
db_session = scoped_session(sessionmaker(autocommit=True, bind=engine))
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
我还使用 uwsgi 和 8 个 worker 将我的应用程序绑定到 nginx。
有时我会在请求期间收到错误 "MySQL server has gone away"。当我重复请求时,我没有错误,但如果我再次重复,我又会出错。多次重复请求后,问题消失。
它发生在应用程序重新启动后(并非总是如此),有时发生在 MySQL 重新启动后。
This 回答建议每次使用后关闭会话,我认为在 teardown_appcontext 上删除会话是一回事。
我也手动尝试过,但没有用。
以下解决方案适合我。服务器连接错误在自定义 Session 和 Query 的方法中处理(对于 ORM 查询)类.
from sqlalchemy import exc, create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker, Session, Query
MAX_RETRIES = 2
class RetryQuery(Query):
def _execute_and_instances(self, *args, **kwargs):
retry = MAX_RETRIES
while retry:
retry -= 1
try:
return super(RetryQuery, self)._execute_and_instances(*args, **kwargs)
except exc.SQLAlchemyError as e:
if e.orig.args[0] in (2006, 2013, 2014, 2045, 2055) and retry and e.connection_invalidated:
continue
else:
raise
class RetrySession(Session):
def __init__(self, *args, **kwargs):
kwargs['query_cls'] = RetryQuery
super(RetrySession, self).__init__(*args, **kwargs)
def execute(self, *args, **kwargs):
retry = MAX_RETRIES
while retry:
retry -= 1
try:
return super(ReconnSession, self).execute(*args, **kwargs)
except exc.SQLAlchemyError as e:
if e.orig.args[0] in (2006, 2013, 2014, 2045, 2055) and retry and e.connection_invalidated:
continue
else:
raise
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, pool_recycle=3600, isolation_level='READ_COMMITTED')
db_session = scoped_session(class_=RetrySession, sessionmaker(autocommit=True, bind=engine))
db_session.execute("SELECT col FROM table")
db_session.query(User).all()
我找到了这个问题的真正原因:8个uwsgi worker占用了共享内存。将 lazy-apps = true
添加到 uwsgi 配置后 MySQL 连接正常。
我的 Flask 应用程序中有一个到 MariaDB (MySQL) 服务器的 SQLAlchemy 连接。我使用范围会话,并在应用程序上下文拆卸时删除会话。
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, pool_recycle=3600, isolation_level='READ_COMMITTED')
db_session = scoped_session(sessionmaker(autocommit=True, bind=engine))
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
我还使用 uwsgi 和 8 个 worker 将我的应用程序绑定到 nginx。
有时我会在请求期间收到错误 "MySQL server has gone away"。当我重复请求时,我没有错误,但如果我再次重复,我又会出错。多次重复请求后,问题消失。 它发生在应用程序重新启动后(并非总是如此),有时发生在 MySQL 重新启动后。
This 回答建议每次使用后关闭会话,我认为在 teardown_appcontext 上删除会话是一回事。
我也手动尝试过
以下解决方案适合我。服务器连接错误在自定义 Session 和 Query 的方法中处理(对于 ORM 查询)类.
from sqlalchemy import exc, create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker, Session, Query
MAX_RETRIES = 2
class RetryQuery(Query):
def _execute_and_instances(self, *args, **kwargs):
retry = MAX_RETRIES
while retry:
retry -= 1
try:
return super(RetryQuery, self)._execute_and_instances(*args, **kwargs)
except exc.SQLAlchemyError as e:
if e.orig.args[0] in (2006, 2013, 2014, 2045, 2055) and retry and e.connection_invalidated:
continue
else:
raise
class RetrySession(Session):
def __init__(self, *args, **kwargs):
kwargs['query_cls'] = RetryQuery
super(RetrySession, self).__init__(*args, **kwargs)
def execute(self, *args, **kwargs):
retry = MAX_RETRIES
while retry:
retry -= 1
try:
return super(ReconnSession, self).execute(*args, **kwargs)
except exc.SQLAlchemyError as e:
if e.orig.args[0] in (2006, 2013, 2014, 2045, 2055) and retry and e.connection_invalidated:
continue
else:
raise
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, pool_recycle=3600, isolation_level='READ_COMMITTED')
db_session = scoped_session(class_=RetrySession, sessionmaker(autocommit=True, bind=engine))
db_session.execute("SELECT col FROM table")
db_session.query(User).all()
我找到了这个问题的真正原因:8个uwsgi worker占用了共享内存。将 lazy-apps = true
添加到 uwsgi 配置后 MySQL 连接正常。