我正在使用 Python、Flask 和 SQLAlchemy。 我在构建应用程序时一直使用本地数据库,并且它在以下代码中运行良好:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config[
'SQLALCHEMY_DATABASE_URI'] = \
'mysql+pymysql://<username>:<password>@localhost/<DBName>'
db = SQLAlchemy(app)
现在,我正在尝试使用 Python 包 sshtunnel 获取此代码以连接到远程数据库。这段代码如下所示:
from flask import Flask
from sshtunnel import SSHTunnelForwarder
from flask_sqlalchemy import SQLAlchemy
forwarding_server = SSHTunnelForwarder(
'1.2.3.4', #my host IP address
ssh_username="user",
ssh_password="password",
remote_bind_address=('127.0.0.1', 8080)
)
forwarding_server.start()
local_port = str(forwarding_server.local_bind_port)
app = Flask(__name__)
app.config[
'SQLALCHEMY_DATABASE_URI'] = \
'mysql+pymysql://<username>:<password>@127.0.0.1:' + local_port + '/<DBName>'
db = SQLAlchemy(app)
看起来像是正在连接,但在启动我的烧瓶应用程序后,我立即收到以下错误:
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2013, 'Lost connection to MySQL server during query')
对这个问题的任何见解都会有所帮助。预先感谢!
我尝试了 pool_recycle 值小于 wait_timeout (mariadb 数据库使用 50),但我不知道为什么它对我不起作用!在 try / except 块中使用显式的 pre_ping 和会话回滚/关闭对我有用。我使用下面的这个装饰器来包装我的所有函数,这解决了我的问题:
def manage_session(f):
def inner(*args, **kwargs):
# MANUAL PRE PING
try:
db.session.execute("SELECT 1;")
db.session.commit()
except:
db.session.rollback()
finally:
db.session.close()
# SESSION COMMIT, ROLLBACK, CLOSE
try:
res = f(*args, **kwargs)
db.session.commit()
return res
except Exception as e:
db.session.rollback()
raise e
# OR return traceback.format_exc()
finally:
db.session.close()
return inner
然后装饰我的函数如下:
@manage_session
def my_function(*args, **kwargs):
return "result"
当您指定 SQLALCHEMY_DATABASE_URI 时,您告诉flask_sqlalchemy 这是一个在 app_context 上断言的开放会话。
退出 app_context 时 flask_sqlalchemy 正在关闭活动会话,包括您的数据库会话
我不知道您的应用程序的流程,但请尽力在退出 app_context 之前关闭数据库会话,并确保您确切知道在 app_context 中创建和使用新数据库会话的位置。