news 2026/4/3 4:09:43

FastAPI 流式响应中,如何优雅处理客户端断连后的数据库操作?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FastAPI 流式响应中,如何优雅处理客户端断连后的数据库操作?

问题出现过程

1. 客户端发起流式对话请求

我们从一个典型的流式对话接口开始。我们使用依赖注入来获取一个 SQLAlchemy 的 AsyncSession,在对话开始时创建消息,在对话结束后更新 AI 的回答。

流式对话原始代码(伪代码)

2. 客户端取消对话(主动断开)

当用户取消发送时,会抛出这个异常

pymysql.err.InterfaceError:

原因:当客户端断开时 ,FastAPI 会立即把它的 session连接回收掉,底层的那个物理连接被标记为 Cancelled,然后执行finally的时候,再往下传原来session连接就不对了,save_conversation函数就会抛pymysql.err.InterfaceError。

问题解决尝试

尝试一:在 save_conversation 函数中创建新连接

一个自然的想法是:既然旧的 session 不能用了,那就在保存的时候检查一下,如果不可用就创建一个新的。

代码更新

结果:失败! 没想到,即使创建了新的 session,依然抛出了 pymysql.err.InterfaceError。

原因分析:之所以还会抛错误,原因是这个新会话 依然在使用已经被取消的连接池资源,因为 FastAPI/Starlette 在主请求取消时,会把整个 AsyncSessionLocal() 对象的连接都标记为 “cancelled”。即便你重新 async with AsyncSessionLocal(),底层复用的还是同一个数据库连接池里的连接,而那个连接刚被 cancel。

重新创建个数据库引擎 是肯定可以的,但是只是对话后更新,这么搞完全没必要。

或者创建个独立线程,在新线程中去创建新连接,应该是可以的,个人还是感觉比较重,浪费资源。

尝试二:创建个协程去执行save_conversation

代码更新

疑惑点:asyncio.create_task 启动的协程仍然跑在同一个线程和进程里,也会复用那个全局的连接池,理论上确实还有可能拿到刚才那个被 cancel 的连接啊?

主要在于操作的时序和上下文隔离:

先清理,后执行

当原始请求被取消后,FastAPI 会立即开始清理与该请求相关的资源(包括回收它持有的数据库连接)。这个清理动作在 finally 块中调用 create_task 之前就已经触发了。我们派生出的后台任务是在这个清理逻辑之后才启动的。

上下文隔离

这个后台协程已经完全不挂在 HTTP 请求的上下文上了。客户端断开与否,都影响不了它的独立运行。只要连接池中还有任意一个好连接,它就能完成写入。

高成功率

因为顺序已经变成了:先断开、先清理 → 再新建、再执行,所以新任务向连接池请求时,拿到那个“坏掉”连接的概率已经大大降低。连接池会优先分配一个健康的、空闲的连接。

即使在极端情况下又拿到了旧连接,它也很有可能在 session.begin() 阶段就失败,我们还可以在后台任务的 try...except 块里加入重试逻辑(比如 await asyncio.sleep(0.1) 后重试),进一步提高健壮性。

大量测试后,发现真没问题😁。

结论

对于需要确保最终操作(如数据写入)一定执行的流式 API,asyncio.create_task 提供了一种轻量级且非常有效的解决方案。它避免了引入像 Celery 这样复杂的任务队列,同时优雅地解决了因客户端断连导致资源状态污染的问题。

通过将关键的收尾工作与不稳定的 HTTP 请求生命周期解耦,我们能构建出更加健壮和可靠的 FastAPI 应用。"

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 6:26:27

是德科技 E8267D 信号发生器多通道相位相干实现方案

相位相干是指多个信号之间保持固定的相位关系。在许多应用中,需要生成多个相位相干的射频信号,例如: 相控阵雷达: 通过控制阵列中每个天线单元的信号相位,实现波束扫描和成形。多天线通信 (MIMO): 利用多个…

作者头像 李华
网站建设 2026/4/1 0:09:13

使用是德DSOX1204A快速捕捉与调试信号的技巧

是德DSOX1204A示波器是一款功能强大的数字示波器,具备先进的技术和用户友好的界面,使得它在信号捕捉与调试方面表现出色。它具有高达200MHz的带宽和4个通道的能力,使得工程师和技术人员可以有效地分析复杂的电信号。本文将探讨如何利用DSOX12…

作者头像 李华
网站建设 2026/4/1 3:38:55

一个赚了16倍的老股民的惨痛反思:原来,我只是个幸存者?

引言:从市场神话到自我怀疑在投资的世界里,没有什么比一连串的辉煌胜利更能塑造一个人的信念了。当你亲手将一笔资金在4年内翻10倍,10年内翻16倍时,那种感觉近乎封神。你所选择的策略不再是策略,而是真理;你…

作者头像 李华
网站建设 2026/3/31 1:54:27

【毕业设计】基于springboot+微信小程序的公务员助学系统小程序的设计与实现(源码+文档+远程调试,全bao定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/4/1 1:47:52

[python]FastAPI - 全链路日志追踪 Tracking ID 的设计

在实际业务中,根据 tracking_id 追查日志中一条请求的完整处理路径是一个比较常见的需求。不过 FastAPI 官方并没有提供相对应的功能,因此需要开发者自行实现。本文介绍如何基于 contextvars,为每次请求的完整流程都添加一个 tracking_id&…

作者头像 李华