【问题标题】:uWSGI, Flask, sqlalchemy, and postgres: SSL error: decryption failed or bad record macuWSGI、Flask、sqlalchemy 和 postgres:SSL 错误:解密失败或坏记录 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 的分叉有关。但除此之外,我不清楚需要做什么。

【问题讨论】:

    标签: postgresql nginx sqlalchemy flask 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
        • 我想知道你是怎么想出来的?调试过程是怎样的?
        猜你喜欢
        • 2021-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-26
        • 1970-01-01
        • 2013-07-05
        • 2015-06-13
        • 1970-01-01
        相关资源
        最近更新 更多