从 TCP 到 JSON:一次 FastAPI + LLM 生产环境 “Unexpected end of JSON input” 的底层剖析

🔥 从 TCP 到 JSON:一次 FastAPI + LLM 生产环境 "Unexpected end of JSON input" 的底层剖析

关键词:TCP 连接断开、HTTP 分块传输、Nginx 超时机制、FastAPI、LLM 长耗时任务、生产环境架构


一、问题现象

线上部署 FastAPI + AsyncOpenAI 接口后,前端报错:

text 复制代码
Failed to execute 'json' on 'Response': Unexpected end of JSON input

浏览器控制台:

text 复制代码
SyntaxError: Unexpected end of JSON input

但:

  • 后端日志显示 LLM 请求成功
  • HTML 文件已成功生成
  • 本地 curl 测试完全正常

这意味着:

JSON 本身没有问题

服务器代码没有报错

但客户端收到了不完整的响应


二、问题的真正入口:TCP 连接被强制关闭

我们从最底层开始分析。


1️⃣ HTTP 本质上是 TCP 之上的协议

一次请求的完整链路:

复制代码
Browser
   ↓
TCP 三次握手
   ↓
HTTP 请求发送
   ↓
Server 处理
   ↓
HTTP 响应返回
   ↓
TCP 连接关闭

只要 TCP 连接被中断:

  • HTTP 响应体会被截断
  • 客户端会收到不完整数据

三、线上环境与本地环境的差异

本地结构

复制代码
Browser
   ↓
Uvicorn (FastAPI)
   ↓
LLM

线上结构

复制代码
Browser
   ↓
Nginx
   ↓
Uvicorn
   ↓
LLM

这里多了一层:

复制代码
Nginx 反向代理

四、Nginx 的超时机制(关键)

默认 Nginx 配置:

nginx 复制代码
proxy_read_timeout 60s;

含义:

如果后端在 60 秒内没有返回完整响应

Nginx 会主动关闭 TCP 连接


五、长耗时 LLM 调用触发条件

LLM 生成耗时:

复制代码
≈ 100 秒

而 Nginx 超时:

复制代码
60 秒

发生的实际网络行为:


时间线(TCP 级别)

复制代码
t=0   Browser 发起请求
t=1   请求到达 Nginx
t=2   Nginx 转发给 FastAPI
t=3   FastAPI 调用 LLM
t=60  Nginx 等待超时
t=60  Nginx 发送 FIN/RST
t=100 FastAPI 准备返回 JSON
t=100 连接已断开

六、TCP 层发生了什么?

当 Nginx 超时:

复制代码
发送 TCP FIN 或 RST 包

这意味着:

  • TCP 连接被关闭
  • FastAPI 仍然在计算
  • Browser 收到半截 HTTP 响应

七、为什么会出现 JSON 解析错误?

假设服务器本来要返回:

json 复制代码
{
  "status": "success",
  "html": "<!DOCTYPE html>..."
}

但连接在传输过程中被中断:

浏览器实际收到:

json 复制代码
{
  "status": "success",
  "ht

于是执行:

javascript 复制代码
await response.json()

解析到一半 → JSON 结构不完整 → 抛出:

复制代码
Unexpected end of JSON input

八、为什么服务器日志看不出问题?

因为:

  • FastAPI 没报错
  • LLM 调用成功
  • 文件写入成功

错误发生在:

TCP 连接已经被 Nginx 提前关闭

属于:

复制代码
网络层错误,而不是应用层错误

九、如何验证是 TCP 层问题?

可以使用:

bash 复制代码
tcpdump -i any port 8000

或者:

bash 复制代码
ss -tn state established

你会看到:

  • 连接在 60 秒时被关闭
  • 后端仍然在运行

十、HTTP 与 TCP 的分层问题

很多开发者会混淆:

复制代码
JSON 错误

实际上是:

复制代码
TCP 连接异常

这属于典型的:

上层协议误报底层错误


十一、为什么不能简单改超时时间?

你可以修改:

nginx 复制代码
proxy_read_timeout 300s;

但会产生问题:

  • 长时间占用 worker
  • 高并发时资源耗尽
  • 连接池阻塞
  • 服务器扩展性差

本质问题仍然存在:

HTTP 请求生命周期过长


十二、正确架构:脱离 HTTP 生命周期

改造为:

复制代码
提交任务 → 秒回 job_id
后台执行 → 更新状态
前端轮询 → 查询结果

网络层变化:


改造后的 TCP 行为

复制代码
Browser
   ↓ POST (50ms)
Nginx
   ↓
FastAPI → create_task
   ↓
立即返回 JSON(job_id)
TCP 正常关闭

后台任务独立执行:

复制代码
FastAPI worker
   ↓
LLM

前端每 1.5 秒发起新请求:

复制代码
GET /status/{job_id}

每次都是短连接。


十三、架构对比(TCP 视角)

❌ 同步模式

复制代码
一个 TCP 连接持续 100 秒

✅ 异步模式

复制代码
多个短 TCP 连接
每个 < 100ms

十四、底层原理总结

当你看到:

复制代码
Unexpected end of JSON input

请优先怀疑:

  • 代理层超时
  • TCP 被中断
  • 连接被重置
  • 网关断开

而不是:

  • JSON 编码
  • Unicode 问题
  • FastAPI 序列化

十五、深入一点:RST 与 FIN 的区别

  • FIN:正常关闭连接
  • RST:强制重置连接

Nginx 在超时场景下通常发送 RST。

客户端收到 RST:

  • 立即中断读取
  • 抛出异常

十六、工程启示

1️⃣ 生产环境必须理解网络层

开发者往往只关注:

复制代码
Python 代码是否报错

但生产问题往往发生在:

复制代码
TCP / 代理层 / 负载均衡层

2️⃣ 长耗时任务必须异步

只要:

复制代码
任务 > 30 秒

就应该:

复制代码
脱离 HTTP 生命周期

十七、最终架构图

复制代码
Browser
   ↓ POST (短连接)
Nginx
   ↓
FastAPI (create_task)
   ↓
后台 Worker
   ↓
LLM


相关推荐
oG99bh7CK1 小时前
FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
数据库·postgresql·fastapi
爱吃生蚝的于勒1 小时前
【Linux】重中之重!TCP协议
linux·运维·服务器·网络·学习·tcp/ip
楼田莉子1 小时前
Linux网络:TCP协议
linux·运维·服务器·网络·tcp/ip
杨云龙UP1 小时前
Oracle 19c:RMAN Duplicate异机复制数据库实操_20260402
linux·运维·服务器·数据库·网络协议·tcp/ip·oracle
IeE1QQ3GT2 小时前
FastAPI + SQLite:从基础CRUD到安全并发的实战指南
安全·sqlite·fastapi
Flamingˢ2 小时前
YNQ + OV5640 视频系统开发(二):OV5640_Data IP 核源码解析
arm开发·嵌入式硬件·网络协议·tcp/ip·fpga开发·vim·音视频
taWSw5OjU2 小时前
FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀
缓存·postgresql·fastapi
PieroPc2 小时前
一个为 AI 助手设计的进销存管理系统,内置完整的 CLI 命令接口,让 AI 可以通过自然语言或命令行直接操作库存。技术栈 FastAPI+Html
人工智能·html·fastapi·cli
YYYing.3 小时前
【Linux/C++网络篇(二) 】TCP并发服务器演进史:从多进程到Epoll的进化指南
linux·服务器·网络·c++·tcp/ip
djBe17esS3 小时前
实战:Java 日志中打印服务器 IP,快速区分多服务器日志归属
java·服务器·tcp/ip