【问题标题】:FastApi sqlalchemy Connection was closed in the middle of operationFastApi sqlalchemy 连接在操作过程中关闭
【发布时间】:2022-01-24 19:36:53
【问题描述】:

我有一个带有异步 sqlalchemy 的异步 FastApi 应用程序,源代码(不会提供 schemas.py,因为它不是必需的):

数据库.py

​​>
from sqlalchemy import (
    Column,
    String,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.decl_api import DeclarativeMeta

from app.config import settings


engine = create_async_engine(settings.DATABASE_URL)
Base: DeclarativeMeta = declarative_base()
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)


class Titles(Base):
    __tablename__ = "titles"
    id = Column(String(100), primary_key=True)
    title = Column(String(100), unique=True)


async def get_session() -> AsyncSession:
    async with async_session() as session:
        yield session

routers.py

​​>
import .database
from fastapi_utils.cbv import cbv
from fastapi_utils.inferring_router import InferringRouter


router = InferringRouter()


async def get_titles(session: AsyncSession):
    results = await session.execute(select(database.Titles)))
    return results.scalars().all()


@cbv(router)
class TitlesView:
    session: AsyncSession = Depends(database.get_session)

    @router.get("/titles", status_code=HTTP_200_OK)
    async def get(self) -> List[TitlesSchema]:
        results = await get_titles(self.session)
        return [TitlesSchema.from_orm(result) for result in results]

main.py

​​>
from fastapi import FastAPI

from app.routers import router 


def create_app() -> FastAPI:
    app = FastAPI()
    app .include_router(routers, prefix="/", tags=["Titles"])

    return printer_app


app = create_app()

它与 docker 一起运行:

CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000", "--limit-max-requests", "10000"]

而且它在 docker 中也有带有默认设置的 Postgres 数据库。这一切都在 docker-swarm 上运行。起初工作正常,接受所有请求。但是如果你等了15-30分钟(我没算),然后再提出请求,就不行了:

<class 'asyncpg.exceptions.ConnectionDoesNotExistError'>: connection was closed in the middle of operation

然后我发送下一个请求并且它不会引发错误。会是什么呢?如何摆脱 ConnectionDoesNotExistError?

【问题讨论】:

  • 在 postgresql 容器中我看到:LOG:无法从客户端接收数据:连接被对等方重置

标签: python sqlalchemy fastapi uvicorn asyncpg


【解决方案1】:

我使用 pool_pre_ping 这样的设置解决了这个问题:

engine = create_engine(DB_URL, pool_pre_ping=True)

https://docs.sqlalchemy.org/en/14/core/pooling.html

【讨论】:

    【解决方案2】:

    我将引用here的答案,我认为它可能有用。全部归功于 q210。

    在我们的例子中,根本原因是 swarm 用来路由数据包的 ipvs 将空闲连接的默认过期时间设置为 900 秒。因此,如果连接超过 15 分钟没有活动,ipvs 就会中断它。 900 秒明显少于大多数可以发送 keepalive tcp 数据包以防止连接空闲的服务使用的默认 linux tcp keepalive 设置(7200 秒)。

    这里描述了同样的问题moby/moby#31208

    为了解决这个问题,我们必须在 postgresql.conf 中设置以下内容:

    tcp_keepalives_idle = 600               # TCP_KEEPIDLE, in seconds;
                                           # 0 selects the system default
    tcp_keepalives_interval = 30            # TCP_KEEPINTVL, in seconds;
                                           # 0 selects the system default
    tcp_keepalives_count = 10               # TCP_KEEPCNT;
                                           # 0 selects the system default
    

    这些设置迫使 PostgreSQL 通过比 ipvs 默认设置更频繁地发送 keepalive 数据包来防止连接空闲(遗憾的是,我们无法在 docker-swarm 中更改)。

    我想同样可以通过更改相应的 linux 设置(net.ipv4.tcp_keepalive_time 等)来实现,因为 PostgreSQL 默认使用它们,但在我们的例子中更改这些设置有点麻烦。

    【讨论】:

    • 谢谢,但我看到了这个帖子,是的,它可能对某人有帮助。我有完全相同的应用程序,但是在 Flask 中,具有默认的 DB 设置并且没有错误。所以我不想更改 DB 设置,而是想以编程方式解决这个错误。
    猜你喜欢
    • 1970-01-01
    • 2015-07-28
    • 2021-09-03
    • 2013-03-21
    • 1970-01-01
    • 2020-02-29
    • 2019-12-28
    • 2022-10-05
    相关资源
    最近更新 更多