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)

uwsgisupervisor 管理,它有一个配置:

[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 在主进程中初始化应用程序,然后将应用程序复制到每个工作进程。问题是如果您在初始化应用程序时打开了数据库连接,那么就会有多个进程共享同一个连接,从而导致上述错误。

解决方法是设置lazyconfiguration 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的主要内容,如果未能解决你的问题,请参考以下文章

Ubuntu18部署uwsgi+flask应用

flask uwsgi nginx

使用Nginx和uwsgi部署Flask项目

nginx+uwsgi+flask搭建后怎么访问.py文件

flask-sqlalchemy 和sqlalchemy的区别

使用flask和flask-socketio配置nginx、uwsgi