Flask-SQLAlchemy:在回滚无效事务之前无法重新连接

Flask-SQLAlchemy: Can't reconnect until invalid transaction is rolled back

所以我将 Amazon Web Services RDS 用于 运行 MySQL 服务器,并将 Python 的 Flask 框架用于 运行 应用程序服务器和 Flask-SQLAlchemy与 RDS 接口。

我的应用程序config.py

SQLALCHEMY_DATABASE_URI = '<RDS Host>'
SQLALCHEMY_POOL_RECYCLE = 60

我的__初始化__.py

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

application = Flask(__name__)
application.config.from_object('config')
db = SQLAlchemy(application)

我有我的主application.py

from flask import Flask
from application import db
import flask.ext.restless
from application.models import Person

application = Flask(__name__)
application.debug=True
db.init_app(application)

@application.route('/')
def index():
    return "Hello, World!"

manager = flask.ext.restless.APIManager(application, flask_sqlalchemy_db=db)
manager.create_api(Person, methods=['GET','POST', 'DELETE'])

if __name__ == '__main__':
    application.run(host='0.0.0.0')

models.py

class Person(db.Model):
    __bind_key__= 'people'
    id = db.Column(db.Integer, primary_key=True)
    firstName = db.Column(db.String(80))
    lastName = db.Column(db.String(80))
    email = db.Column(db.String(80))

    def __init__(self, firstName=None, lastName=None, email=None):
        self.firstName = firstName
        self.lastName = lastName
        self.email = email

然后我有一个脚本来填充数据库以在数据库创建和应用程序启动后进行测试:

from application import db
from application.models import Person

person = Person('Bob', 'Jones', 'bob@website.net')
db.session.add(person)
db.session.commit()

使用 db.drop_all() 和 db.create_all() 重置数据库后,我启动 application.py,然后启动脚本来填充数据库。

服务器将以正确的 JSON 响应,但如果我在数小时后回来检查它,我会收到需要回滚的错误,有时会收到 MySQL 服务器出现的 2006 错误走了。

人们建议我更改 MySQL 服务器上的超时设置,但没有解决任何问题。这是我的设置:

innodb_lock_wait_timeout = 3000
max_allowed_packet       = 65536
net_write_timeout        = 300
wait_timeout             = 300

然后当我查看 RDS 监视器时,它显示 MySQL 服务器保持连接打开了很长一段时间,直到超时。如果我错了,请纠正我,但连接完成后不应该关闭吗?应用服务器似乎一直在确保数据库连接存在,然后当 MySQL 服务器超时时,Flask/Flask-SQLAlchemy 抛出错误并关闭应用服务器。

欢迎任何建议,谢谢!

我想它添加了什么

db.init_app(application)

在 application.py 中,从那以后就没有错误了。

或者,在填充数据库的脚本末尾使用它:

db.session.close()

这应该可以防止那些恼人的 "MySQL server has gone away" 错误。

每次检查是否回滚很麻烦..

我制作了需要提交的插入、更新功能。

@app.teardown_request
def session_clear(exception=None):
    Session.remove()
    if exception and Session.is_active:
        Session.rollback()

此处缺少池回收,因为 MySql 一段时间后关闭会话,因此您需要添加池回收,以便池中的连接在池回收时间后重新连接。

app.config['SQLALCHEMY_POOL_RECYCLE'] = 3600

当您将 sqlalchemy 引擎创建为单例时,通常会出现此错误。在这种情况下,在连接失效后(在我的例子中是 3600 秒),您会收到 InvalidTransaction 错误。

最好的建议是在应用程序初始化时初始化数据库会话

db.init_app(app)

并在需要执行某些 CRUD 操作时导入此数据库会话。

从未遇到过这个问题post我的应用程序有这个变化。

一开始似乎不是交易的问题,但这可能是由于事先 MySQL 错误 Connection reset by peer 引起的。这意味着您的连接丢失,可能是因为您的应用程序上下文设置不正确。

一般来说,最好使用 factory pattern 来创建您的应用程序。这样有很多好处,你的代码是

  • 更易于阅读和设置
  • 更容易测试
  • 避免循环导入

为防止无效事务错误(可能由 OperationalError: Connection reset by peer 引起),您应该确保正确处理数据库连接。

以下示例基于 this article,它很好地解释了 flask 应用程序上下文以及如何将其与数据库连接或任何其他扩展一起使用。

application.py


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

def create_app():
    """Construct the core application."""
    application = Flask(__name__)
    application.config.from_object('config')    # Set globals
    db = SQLAlchemy()

    with application.app_context():
        # Initialize globals/extensions in app context
        db.init_app(app)        

        # import routes here
        from . import routes

    return application


if __name__ == "__main__":
   app = create_app()
   app.run(host="0.0.0.0")

routes.py

from flask import current_app as application

@application.route('/', methods=['GET'])
def index():
   return "Hello, World!"

如果您仍然 运行 进入 disconnect-problems,您还应该查看 dealing with disconnects and have a look at this question 上的 SQLAlchemy 文档。