【发布时间】:2021-08-24 14:46:21
【问题描述】:
我有一个使用 FastAPI 和 SQLAlchemy 构建的 Web 应用程序,它在 Docker 本地运行良好,但在 DigitalOcean 上使用托管 Postgres DB 时数据库查询失败并出现错误:
(psycopg2.OperationalError) 服务器意外关闭连接\n\t这可能意味着服务器异常终止\n\t在处理请求之前或期间。\n\n(此错误的背景:http://sqlalche.me/e/14/e3q8)"}
我之前在使用 Flask 时遇到过这个错误,问题是我必须设置引擎选项 pool_pre_ping=True 并将我的集群/droplet IP 添加到数据库的可信来源。但看起来使用 FastAPI 是不够的。我还能做些什么来成功执行查询?
背景
- Python 3.9
- DigitalOcean 托管 Postgres 13
- psycopg==2.8.6 但也尝试了 2.8.5(对我来说 Flask 在类似情况下 100% 有效)和 2.7.4 以防万一
- 我设置了
pool_pre_ping=True- 我在使用
session.get_bind().pool._pre_ping的请求之前检查了它是否真的设置为True,它实际上是True
- 我在使用
- 我检查了我的集群节点的 IP 是否在数据库可信来源中
- 我使用一个
uvicorn.workers.UvicornH11Workerworker 运行带有 gunicorn 的应用程序 - 我使用中间件在 FastAPI 端点中访问我的数据库会话,如下所示:
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)):
...
- 此外,我尝试将全局定义的引擎与会话上下文一起使用(只是为了检查),但仍然具有相同的行为,因此看起来中间件不是问题
- Postgres 端没有有用的日志
- 我还尝试将我的应用查询更改为
db.execute('SELECT 1'),以防出现一些奇怪的超时或其他问题 - 还是一样 - 我阅读了很多关于 psycopg2 的类似问题,而关于我能找到的 FastAPI 的问题很少,例如this 和 that 当然还有官方文档。
经过所有尝试,问题仍然存在。我不太了解async Python,所以我怀疑问题可能在于连接的共享方式或其他问题(但我目前只使用一名工作人员)。
更新
我尝试切换到asyncpg(文档:https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html)。也可以在本地工作,但在 DigitalOcean 查询挂起时,我收到以下错误:
[Errno 104] Connection reset by peer
看起来原因相同,但 asyncpg 的错误看起来不同。
还尝试在 DigitalOcean 上创建连接池并连接到它 - 仍然是同样的错误。
【问题讨论】:
-
这里使用异步的原因是什么?如果不异步调用,代码会起作用吗?鉴于异步函数理论上可以相对于彼此以任何顺序启动,我认为这是最可能的原因。
-
是的,根据 fastapi 文档,同步示例没有意义。我试图删除
async,但还是一样。此外,它在两种情况下都可以在本地正常工作,并且使用 asyncpg 应该有async并且它也可以按预期在本地工作。只有 DO 托管的数据库才会出现问题。看起来数据库设置不同(但我无法获得托管数据库配置)。同样在 99% 的类似问题中,只需设置pool_pre_ping即可解决问题,并且在 DO 托管数据库的相同设置中也为我解决了问题,但当它是 Flask 应用程序时。
标签: python postgresql sqlalchemy digital-ocean fastapi