FastAPI - (psycopg2.OperationalError) 服务器意外关闭了连接

Max*_*Max 10 python postgresql sqlalchemy digital-ocean fastapi

我有一个使用 FastAPI 和 SQLAlchemy 构建的 Web 应用程序,它在本地使用 Docker 运行正常,但在带有托管 Postgres DB 的 DigitalOcean 上,db 查询失败并出现错误:

(psycopg2.OperationalError) 服务器意外关闭了连接\n\t这可能意味着服务器在处理请求之前或处理请求时异常终止\n\t。\n\n(此错误的背景:http ://sqlalche.me/e /14/e3q8 )"}

我之前在使用 Flask 时遇到过这个错误,问题是我必须设置引擎选项pool_pre_ping=True并将我的 cluster/droplet IP 添加到数据库的可信来源。但是看起来对于 FastAPI 这还不够。我还能做些什么来成功执行查询?

背景

  • 蟒蛇 3.9
  • DigitalOcean 托管 Postgres 13
  • psycopg==2.8.6 但也尝试了 2.8.5(100% 在与 Flask 类似的情况下对我有用)和 2.7.4 以防万一
  • 我已经pool_pre_ping=True设置
    • 我检查过它确实True在使用请求之前设置为正确session.get_bind().pool._pre_ping,它实际上是True
  • 我检查了我的集群节点的 IP 是否在数据库可信来源中
  • 我使用一名uvicorn.workers.UvicornH11Worker工人使用 gunicorn 运行该应用程序
  • 我使用中间件访问 FastAPI enpoints 中的 db 会话,如下所示:
class DBMiddleware:
    def __init__(self, app, sqlalchemy_uri):
        self.app = app
        self.sqlalchemy_uri = sqlalchemy_uri
        self.engine = None

    async def __call__(self, scope: Scope, receive: Receive, send: Send):
        if scope['type'] not in ['http', 'websocket']:
            await self.app(scope, receive, send)
            return

        if not self.engine:
            self.engine = create_engine(self.sqlalchemy_uri, pool_pre_ping=True, pool_recycle=3600)

        session = Session(autoflush=False, autocommit=False, bind=self.engine)
        scope['db'] = session
        await self.app(scope, receive, send)
        session.close()


def get_db(request: Request):
    return request.scope.get('db')

...

@app.on_event('startup')
async def startup():
    ...
    app.add_middleware(DBMiddleware, sqlalchemy_uri=config.SQLALCHEMY_DATABASE_URI)

@router.post('/endpoint')
async def endpoint(db: Session = Depends(get_db)):
    ...
Run Code Online (Sandbox Code Playgroud)
  • 此外,我尝试将全局定义的引擎与会话上下文一起使用(只是为了检查),但仍然具有相同的行为,因此看起来中间件不是问题
  • Postgres 方面没有有用的日志
  • 我还尝试将我的应用程序查询更改为以防db.execute('SELECT 1')万一出现一些奇怪的超时或其他事情 - 仍然相同
  • 我读了很多关于 psycopg2 的类似问题,而我可以找到的关于 FastAPI 的文章很少,例如这个那个,当然还有官方文档。

毕竟尝试了问题仍然存在。我不太了解asyncPython,所以我怀疑问题可能出在连接的共享方式或其他方面(但我目前只使用一名工人)。

更新

我试图切换到asyncpg(文档:https : //docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html)。也可以在本地运行,但在 DigitalOcean 上查询挂起,我收到以下错误:

[Errno 104] Connection reset by peer
Run Code Online (Sandbox Code Playgroud)

看起来原因是一样的,但是 asyncpg 的错误看起来不同。

还尝试在 DigitalOcean 上创建一个连接池并连接到它 - 仍然是同样的错误。

Lif*_*lex 11

您是否尝试将任何connect_args添加到sqlalchemy create_engine中?这些参数应该允许您维持与数据库的连接。

以下是可能有用的各种libpq 连接参数的列表。

create_engine(self.sqlalchemy_uri, 
              pool_pre_ping=True, 
              pool_recycle=3600, # this line might not be needed
              connect_args={
                  "keepalives": 1,
                  "keepalives_idle": 30,
                  "keepalives_interval": 10,
                  "keepalives_count": 5,
              }
            )
Run Code Online (Sandbox Code Playgroud)

值得注意的是,任何使用psycopg的人也可以使用这些libpq 连接参数


小智 1

这可能是一个有点笼统的答案,但以下步骤将有助于定位问题:

  1. 尝试通过 SSH 连接,这样您就可以确保您的请求有效并且原因不在数据库中。
  2. 检查连接的端口号,看看它们是否与 uvicorn、Digital Ocean 设置和 python 脚本匹配。
  3. 尝试暂时允许来自任何 IP 的访问 - 也许某些奇怪的路由不允许您访问