【问题标题】:how to fix "OperationalError: (psycopg2.OperationalError) server closed the connection unexpectedly"如何修复“OperationalError:(psycopg2.OperationalError)服务器意外关闭连接”
【发布时间】:2021-05-06 23:44:29
【问题描述】:

服务

我的服务基于flask + postgresql + gunicorn + supervisor + nginx

docker部署的时候,运行服务后,再访问api,有时候报错,有时候还可以。

并且sqlachemy连接数据库添加参数'sslmode:disable'

File "/usr/local/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 287, in _execute_on_connection
    Return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1107, in _execute_clauseelement
    Distilled_params,
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1248, in _execute_context
    e, statement, parameters, cursor, context
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1466, in _handle_dbapi_exception
    Util.raise_from_cause(sqlalchemy_exception, exc_info)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 383, in raise_from_cause
    Reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1244, in _execute_context
    Cursor, statement, parameters, context
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 552, in do_execute
    Cursor.execute(statement, parameters)
OperationalError: (psycopg2.OperationalError) server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.

信息

Docker for Mac:版本:2.0.0.3 (31259)

macOS:版本 10.14.2

Python:版本 2.7.15

递归方法

通过命令查看端口信息时

lsof -i:5432

5432端口是postgresql数据库默认端口,如果输出控制台是

COMMAND    PID        USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
postgres 86469 user    4u  IPv6 0xxddd      0t0  TCP *:postgresql (LISTEN)
postgres 86469 user    5u  IPv4 0xxddr      0t0  TCP *:postgresql (LISTEN)

它会显示错误信息:

OperationalError: (psycopg2.OperationalError) server closed the connection unexpectedly

但如果 outputconsolelog 显示如下:

COMMAND     PID        USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
com.docke 62421 user   26u  IPv4 0xe93      0t0  TCP 192.168.2.7:6435->192.168.2.7:postgresql (ESTABLISHED)
postgres  86460 user    4u  IPv6 0xed3      0t0  TCP *:postgresql (LISTEN)
postgres  86460 user    5u  IPv4 0xe513      0t0  TCP *:postgresql (LISTEN)
postgres  86856 user   11u  IPv4 0xfe93      0t0  TCP 192.168.2.7:postgresql->192.168.2.7:6435 (ESTABLISHED)

这种情况,api 会很好用。

因为 Docker for mac?

参考链接https://github.com/docker/for-mac/issues/2442,问题解决不了我的问题。

注意到类似的问题了吗?

参考链接Python & Sqlalchemy - Connection pattern -> Disconnected from the remote server randomly

这个问题也不能解决我的问题。

解决方案

flask_sqlachemy need the parameter pool_pre_ping

from flask_sqlalchemy import SQLAlchemy as _BaseSQLAlchemy

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

db = SQLAlchemy()

【问题讨论】:

  • 如果你使用的是python 3,很可能是因为this issue
  • 谢谢,但我使用的是 Python 2.7.15
  • @ygesher 我在 python3 中遇到了类似的问题。我正在使用 psycopg2==2.8.3 和 SQLAlchemy==1.3.7。

标签: postgresql macos docker flask


【解决方案1】:

sqlalchemy.orm 的逻辑相同(顺便说一下,flask_sqlalchemy 是基于它的)

engine = sqlalchemy.create_engine(connection_string, pool_pre_ping=True)

可以设置更多的保护策略,如文档中所述:https://docs.sqlalchemy.org/en/13/core/pooling.html#disconnect-handling-pessimistic

例如,这是我的引擎实例化:

engine = sqlalchemy.create_engine(connection_string,
                                      pool_size=10,
                                      max_overflow=2,
                                      pool_recycle=300,
                                      pool_pre_ping=True,
                                      pool_use_lifo=True)

sqlalchemy.orm.sessionmaker(bind=engine, query_cls=RetryingQuery)

对于 RetryingQuery 代码,请参阅:Retry failed sqlalchemy queries

【讨论】:

    【解决方案2】:

    我正在发布我自己的答案,因为以上都没有解决我的特定设置(Postgres 12.2、SQLAlchemy 1.3)。

    为了阻止OperationalErrors,我必须将一些额外的connect_args 传递给create_engine

    create_engine(
            connection_string,
            pool_pre_ping=True,
            connect_args={
                "keepalives": 1,
                "keepalives_idle": 30,
                "keepalives_interval": 10,
                "keepalives_count": 5,
            }
        )
    

    【讨论】:

      【解决方案3】:

      基于答案中的 Solution 和来自@MaxBlax360 答案的信息。我认为在 Flask-SQLAlchemy 中设置这些配置值的正确方法是设置 app.config['SQLALCHEMY_ENGINE_OPTIONS']:

      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      app = Flask(__name__)
      # pool_pre_ping should help handle DB connection drops
      app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {"pool_pre_ping": True}  
      app.config['SQLALCHEMY_DATABASE_URI'] = \
          f'postgresql+psycopg2://{POSTGRES_USER}:{dbpass}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DBNAME}'
      db = SQLAlchemy(app)
      

      See also Flask-SQLAlchemy docs on Configuration Keys

      【讨论】:

        【解决方案4】:

        我的数据库配置:

        app = Flask(__name__)
        app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {"pool_pre_ping": True}
        app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
        app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
        
        # Play with following options:
        app.config['SQLALCHEMY_POOL_SIZE'] = 10
        app.config['SQLALCHEMY_MAX_OVERFLOW'] = 20
        app.config['SQLALCHEMY_POOL_RECYCLE'] = 1800
        
        db = SQLAlchemy(app)
        

        【讨论】:

          猜你喜欢
          • 2021-08-24
          • 2018-02-20
          • 2021-04-26
          • 2022-07-28
          • 2018-10-12
          • 1970-01-01
          • 2013-07-12
          • 1970-01-01
          • 2016-12-15
          相关资源
          最近更新 更多