uWSGI、Flask、sqlalchemy 和 postgres:SSL 错误:解密失败或坏记录 mac
Posted
技术标签:
【中文标题】uWSGI、Flask、sqlalchemy 和 postgres:SSL 错误:解密失败或坏记录 mac【英文标题】:uWSGI, Flask, sqlalchemy, and postgres: SSL error: decryption failed or bad record mac 【发布时间】:2014-05-10 06:11:05 【问题描述】:我正在尝试使用 uWSGI + nginx 设置应用程序网络服务器,它运行使用 SQLAlchemy 与 Postgres 数据库通信的 Flask 应用程序。
当我向网络服务器发出请求时,其他所有响应都会出现 500 错误。
错误是:
Traceback (most recent call last):
File "/var/env/argos/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 867, in _execute_context
context)
File "/var/env/argos/lib/python3.3/site-packages/sqlalchemy/engine/default.py", line 388, in do_execute
cursor.execute(statement, parameters)
psycopg2.OperationalError: SSL error: decryption failed or bad record mac
The above exception was the direct cause of the following exception:
sqlalchemy.exc.OperationalError: (OperationalError) SSL error: decryption failed or bad record mac
错误是由一个简单的Flask-SQLAlchemy
方法触发的:
result = models.Event.query.get(id)
uwsgi
由supervisor
管理,它有一个配置:
[program:my_app]
command=/usr/bin/uwsgi --ini /etc/uwsgi/apps-enabled/myapp.ini --catch-exceptions
directory=/path/to/my/app
stopsignal=QUIT
autostart=true
autorestart=true
uwsgi
的配置如下:
[uwsgi]
socket = /tmp/my_app.sock
logto = /var/log/my_app.log
plugins = python3
virtualenv = /path/to/my/venv
pythonpath = /path/to/my/app
wsgi-file = /path/to/my/app/application.py
callable = app
max-requests = 1000
chmod-socket = 666
chown-socket = www-data:www-data
master = true
processes = 2
no-orphans = true
log-date = true
uid = www-data
gid = www-data
我能得到的最远的是它与 uwsgi 的分叉有关。但除此之外,我不清楚需要做什么。
【问题讨论】:
【参考方案1】:不确定是将其添加为该问题的答案,还是提出单独的问题并将其作为答案。由于与发布和回答的人略有不同的原因,我得到了完全相同的错误。在我的设置中,我使用 gunicorn 作为 Flask 应用程序的 wsgi。在这个应用程序中,我将一些密集的数据库操作卸载给了一个 celery worker。错误来自 celery worker。
通过阅读此处的大量答案并查看 psycopg2 以及 sqlalchemy 会话文档,我发现在不同的进程(gunicorn worker 和 sqlalchemy worker)之间共享 SQLAlchemy 会话是一个坏主意就我而言)。
最终为我解决这个问题的是在 celery worker 函数中创建一个新会话,因此每次调用它时都会使用一个新会话,并且在每次 Web 请求后销毁该会话,因此烧瓶每个请求都使用一个会话。整体解决方案如下所示:
Flask_app.py
@app.teardown_appcontext
def shutdown_session(exception=None):
session.close()
celery_func.py
@celery_app.task(bind=True, throws=(IntegrityError))
def access_db(self,entity_dict, tablename):
with Session() as session:
try:
session.add(ORM_obj)
session.commit()
except IntegrityError as e:
session.rollback()
print('primary key violated')
raise e
【讨论】:
【参考方案2】:我正在 Heroku 上使用 gunicorn 运行烧瓶应用程序。当我将 --preload
选项添加到我的 Procfile 时,我的应用程序开始出现此问题。当我删除该选项时,我的应用程序恢复正常运行。
【讨论】:
【参考方案3】:作为替代方案,您可以处置引擎。这就是我解决问题的方法。
如果在创建应用的过程中,即在创建应用本身的模块中存在查询,则可能会出现此类问题。如果这样,引擎会分配一个连接池,然后 uwsgi 分叉。
通过调用“engine.dispose()”,连接池本身会关闭,一旦有人再次开始查询,新连接就会出现。因此,如果您在创建应用的模块末尾执行此操作,则会在 UWSGI 分叉之后创建新连接。
【讨论】:
这对我来说是一个更好的解决方案,因为我使用gunicorn --preload
在分叉之前加载应用程序代码以降低内存使用量,所以我特别不想使用延迟加载。这实际上是 SQLAlchemy 文档中engine.dispose()
的推荐用例:docs.sqlalchemy.org/en/13/core/connections.html#engine-disposal【参考方案4】:
问题最终是 uwsgi 的分叉。
当使用一个主进程处理多个进程时,uwsgi 在主进程中初始化应用程序,然后将应用程序复制到每个工作进程。问题是如果您在初始化应用程序时打开了数据库连接,那么就会有多个进程共享同一个连接,从而导致上述错误。
解决方法是设置lazy
configuration option for uwsgi,强制在每个进程中完整加载应用程序:
lazy
设置惰性模式(在工作人员而不是主服务器中加载应用程序)。
此选项可能会影响内存使用,因为无法使用 Copy-on-Write 语义。当lazy开启时,只有worker会被uWSGI的reload信号重新加载;主人会一直活着。因此,主节点重新加载时不会获取 uWSGI 配置更改。
还有一个lazy-apps
选项:
lazy-apps
在每个worker而不是master中加载应用程序。
此选项可能会影响内存使用,因为无法使用 Copy-on-Write 语义。与lazy不同,这只会影响应用程序的加载方式,不会影响master在重新加载时的行为。
这个 uwsgi 配置最终对我有用:
[uwsgi]
socket = /tmp/my_app.sock
logto = /var/log/my_app.log
plugins = python3
virtualenv = /path/to/my/venv
pythonpath = /path/to/my/app
wsgi-file = /path/to/my/app/application.py
callable = app
max-requests = 1000
chmod-socket = 666
chown-socket = www-data:www-data
master = true
processes = 2
no-orphans = true
log-date = true
uid = www-data
gid = www-data
# the fix
lazy = true
lazy-apps = true
【讨论】:
感谢您回答自己。今天对我有帮助。 “请注意,因为有一个名为lazy
的旧选项更具侵略性且非常不鼓励(它仍然存在只是为了向后兼容)”来源:uwsgi-docs.readthedocs.org/en/latest/ThingsToKnow.html
我想知道你是怎么想出来的?调试过程是怎样的?以上是关于uWSGI、Flask、sqlalchemy 和 postgres:SSL 错误:解密失败或坏记录 mac的主要内容,如果未能解决你的问题,请参考以下文章