我有一个用 Flask+SQLALchemy+Celery 编写的应用程序,RabbitMQ 作为代理,数据库是 PostgreSQL (PostgreSQL 10.11 (Ubuntu 10.11-1.pgdg16.04+1) on x86_64-pc-linux-gnu,由 gcc 编译(Ubuntu 5.4.0-6ubuntu1~16.04.12)5.4.0 20160609,64位)。数据库托管在 DigitalOcean(1 个 CPU、2Gb RAM)中。所有应用程序工作人员(flask 或 celery)都在 Supervisor 中启动。
在我连接数据库的项目中,我使用如下的flask_sqlalchemy包:
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
# Init
app = Flask(__name__)
# Create the connection to database
db = SQLAlchemy(app)
我在 Flask 应用程序中编写了一些登录,测试了它,然后将其复制到 Celery 项目(其中数据库连接是相同的)。所以现在我的示例芹菜任务看起来像这样:
@celery.task(name='example_task', queue='default')
def example_task(payload):
""" Some logic here """
data = ExampleModel.query.filter(ExampleModel.id == payload["id"]).first()
""" Some another app logic """
db.session.add(SecondModel(payload))
db.session.commit()
return {"success": True}
问题是当我在笔记本电脑上运行应用程序时,一切正常,没有错误。当我将我的应用程序上传到 VPS 上并且没有太多用户时,一切都还好。但一段时间后,当有 30 多个用户同时调用此 example_task 时,有时在从数据库中选择一些数据的非常简单的查询中开始定期出现错误:
File "/venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
context)
File "/venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
cursor.execute(statement, parameters)
psycopg2.OperationalError: SSL SYSCALL error: EOF detected
The above exception was the direct cause of the following exception:
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL SYSCALL error: EOF detected
[SQL: 'SELECT example_model.id AS example_model_id, example_model.key AS example_model_key
\nFROM example_model \nWHERE example_model.id = %(id_1)s \n LIMIT %(param_1)s'] [parameters: {'id_1': 2, 'param_1': 1}] (Background on this error at: http://sqlalche.me/e/e3q8)
有时,但非常非常罕见,我在日志中看到此错误:
psycopg2.OperationalError: SSL error: decryption failed or bad record mac
我编写了一个示例异常装饰器来处理错误(例如任何错误,而不仅仅是 SQLAlchemy 错误),在它捕获错误后,我执行了 db.session.rollback()
def exception_log(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as err:
# Do the rollback
db.session.rollback()
# Call function again
return func(*args, **kwargs)
但这对我没有帮助,因为是的,它重新加载了数据库的连接,然后功能工作正常,但是应用程序开始工作得越来越慢,在某些时候我应该在主管中重新加载工作人员。我在 PostgreSQL 中看到很多空闲连接,我将空闲事务超时设置为 5 分钟,但没有帮助。
SET SESSION idle_in_transaction_session_timeout = '5min';
我不知道下一步该做什么,因为现在唯一有帮助的解决方案是每当我看到应用程序运行速度越来越慢时,就在主管中重新加载应用程序工作人员。
我在使用托管 Postgres 数据库服务时遇到了同样的问题,似乎时不时地断开我的连接。
psycopg2.OperationalError:SSL SYSCALL 错误:检测到 EOF
值得庆幸的是,SQLAlchemy 似乎有可以帮助解决此问题的标志。
将
pre_ping=True
传递给 create_engine
,它将在将所有池连接用于实际查询之前检查它们。
悲观方法是指在每次连接池检出开始时对 SQL 连接发出测试语句,以测试数据库连接是否仍然可行。通常,这是一个像“SELECT 1”这样的简单语句,但也可以使用一些特定于 DBAPI 的方法来测试连接的活动性。
engine = create_engine("mysql+pymysql://user:pw@host/db", pool_pre_ping=True)
https://docs.sqlalchemy.org/en/13/core/pooling.html#disconnect-handling-pessimistic
我通过将 Flask-SQLAlchemy 包升级到最新版本修复了这个问题。
如果您的 Flask、Celery 和 PostgreSQL 在同一台服务器上,那么增加 CPU 核心可能会解决问题。