flask_sqlalchemy `pool_pre_ping` 有时只工作

Anu*_*raz 42 python mysql connection-pooling flask flask-sqlalchemy

为了测试,我将MYSQL(RDS)参数修改如下;

wait_timeout = 40(默认为 28800)

max_allowed_pa​​cket = 1GB(最大 - 只是为了确保问题不是由小数据包引起的)

net_read_timeout = 10

Interactive_timeout 不变

然后在没有pool_pre_ping设置选项的情况下测试我的应用程序(默认为 False),使应用程序保持非活动状态 40 秒,尝试登录,然后我得到

Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]: Traceback (most recent call last):
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     context)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 507, in do_execute
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     cursor.execute(statement, parameters)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 206, in execute
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     res = self._query(query)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 312, in _query
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     db.query(q)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/connections.py", line 224, in query
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     _mysql.connection.query(self, query)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]: MySQLdb._exceptions.OperationalError: (2013, 'Lost connection to MySQL server during query')
Run Code Online (Sandbox Code Playgroud)

添加了pool_pre_ping这样的(使用flask_sqlalchamy 2.4.1版);

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy as _BaseSQLAlchemy


class SQLAlchemy(_BaseSQLAlchemy):
    def apply_pool_defaults(self, app, options):
        super(SQLAlchemy, self).apply_pool_defaults(app, options)
        options["pool_pre_ping"] = True
#        options["pool_recycle"] = 30
#        options["pool_timeout"] = 35

db = SQLAlchemy()


class DevConfig():
    SQLALCHEMY_ENGINE_OPTIONS = {'pool_recycle': 280, 'pool_timeout': 100, 'pool_pre_ping': True} # These configs doesn't get applied in engine configs :/
    DEBUG = True
    # SERVER_NAME = '127.0.0.1:5000'
    SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI_DEV')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

config = dict(
    dev=DevConfig,
)

app = Flask(__name__, instance_relative_config=True)
app.config.from_object(config['dev'])

# INIT DATABASE
db.init_app(app)
with app.app_context():
    db.create_all()

-----------run.py
app.run(host='127.0.0.1', port=5000)
Run Code Online (Sandbox Code Playgroud)

有了这个,即使在 MySQL 服务器关闭了以前的连接之后,webapp 现在也设法获得新的连接。当我在服务器关闭数据库后立即访问数据库时,它总是正常工作(最多尝试 50 秒后)...我看到同样的错误。
根据文档,(尤其是处理断开连接部分),该pool_pre_ping选项应该在后台仪式中处理这种情况吗?或者我需要在 MySQL 服务器中更改任何其他超时变量吗?

小智 5

我进行了以下设置:

SQLALCHEMY_ENGINE_OPTIONS = {
    'pool_size': 10,
    'pool_recycle': 60,
    'pool_pre_ping': True
}
Run Code Online (Sandbox Code Playgroud)

最近几个月已经停止下跌了...


Mav*_*vid 3

让我们检查一下手册。来自Flask-SQLAlchemy 配置文档

\n
\n

某些数据库后端可能会施加不同的非活动连接超时,这会干扰 Flask-SQLAlchemy\xe2\x80\x99s 连接池。

\n

默认情况下,MariaDB 配置为 600 秒超时。这\n通常很难调试,只有生产环境的例外\n例如

\n
\n

2013: Lost connection to MySQL server during query.

\n
\n

如果您使用的后端(或预配置的数据库即服务)\n具有较低的连接超时,建议您将\n设置SQLALCHEMY_POOL_RECYCLE为小于后端\xe2\x80\x99s 超时的值。

\n
\n

问题中引用的脚本显示其 MySQL 之间的差异timeout-configs ( wait_timeout, net_read_timeout) 及其 SQLAlchemy( pool_recycle, pool_timeout) 和 Flask-SQLAlchemy超时 ( SQLALCHEMY_POOL_RECYCLE, SQLALCHEMY_POOL_TIMEOUT)。

\n

当您注意到可能会发生变化的重复信息时,请考虑用不太可能发生变化的抽象内容来替换它。这就是软件开发的“不要重复自己”(DRY)原则。

\n

我们可以通过使用DevConfig帮助器类来协调应用程序中的数据库连接配置常量来实现此抽象。通过这种方法,我们将配置分配给静态属性并引用它们,以便不存在冲突的超时期望。这是一个实现:

\n
import os\nfrom flask import Flask\nfrom flask_sqlalchemy import SQLAlchemy as _BaseSQLAlchemy\n\n# Coordinate DevConfig with SQLAlchemy and Flask-SQLAlchemy (don\'t repeat yourself!)\n\nclass DevConfig():\n    SQLALCHEMY_POOL_RECYCLE = 35  # value less than backend\xe2\x80\x99s timeout\n    SQLALCHEMY_POOL_TIMEOUT = 7  # value less than backend\xe2\x80\x99s timeout\n    SQLALCHEMY_PRE_PING = True\n    SQLALCHEMY_ENGINE_OPTIONS = {\'pool_recycle\': SQLALCHEMY_POOL_RECYCLE, \'pool_timeout\': SQLALCHEMY_POOL_TIMEOUT, \'pool_pre_ping\': SQLALCHEMY_PRE_PING}\n    DEBUG = True\n    # SERVER_NAME = \'127.0.0.1:5000\'\n    SQLALCHEMY_DATABASE_URI = os.getenv(\'SQLALCHEMY_DATABASE_URI_DEV\')\n    SQLALCHEMY_TRACK_MODIFICATIONS = False\n\nclass SQLAlchemy(_BaseSQLAlchemy):\n    def apply_pool_defaults(self, app, options):\n        super(SQLAlchemy, self).apply_pool_defaults(app, options)\n        options["pool_pre_ping"] = DevConfig.SQLALCHEMY_PRE_PING\n#        options["pool_recycle"] = 30\n#        options["pool_timeout"] = 35\n\ndb = SQLAlchemy()\n\nconfig = dict(\n    dev=DevConfig,\n)\n\napp = Flask(__name__, instance_relative_config=True)\napp.config.from_object(config[\'dev\'])\n\n# INIT DATABASE\ndb.init_app(app)\nwith app.app_context():\n    db.create_all()\n
Run Code Online (Sandbox Code Playgroud)\n

如果您愿意,您可以检查我所做的更改的差异: diffchecker.com/Q1e85Hhc

\n

如果您想了解有关软件设计原理的更多信息,请查看上面链接的维基百科页面上的参考资料。

\n