博客
文章系列日历
归档关于搜索

鄂ICP备19019526号

© 2026 博客

  1. 文章
  2. LLM 流式推理的协议工程真相 2026:SSE、WebSocket、gRPC streaming 的选型与背压治理

LLM 流式推理的协议工程真相 2026:SSE、WebSocket、gRPC streaming 的选型与背压治理

2026年7月1日·约 15 分钟·4299 字·0 次阅读
AI 原生架构
LLM 流式推理的协议工程真相 2026:SSE、WebSocket、gRPC streaming 的选型与背压治理

目录

  • 一、流式推理为何是 LLM Serving 的"原生模式"
  • 二、协议候选:SSE / WebSocket / gRPC streaming
  • 2.1 Server-Sent Events (SSE)
  • 2.2 WebSocket
  • 2.3 gRPC streaming
  • 2.4 协议对比矩阵
  • 三、反向代理的隐藏陷阱:nginx / envoy / cloudflare
  • 3.1 nginx 的 proxybuffersize
  • 3.2 envoy 的 buffer filter
  • 3.3 cloudflare 的免费层缓存
  • 3.4 ALB / NLB / SLB 的 idle timeout
  • 四、客户端断连与服务端取消协议
  • 4.1 SSE 的取消传播
  • 4.2 WebSocket 的取消传播
  • 4.3 gRPC 的取消传播(最佳)
  • 4.4 取消链的端到端设计
  • 五、背压协议:从 TCP window 到应用层 token bucket
  • 5.1 TCP 层的 backpressure
  • 5.2 HTTP/2 flow control
  • 5.3 应用层 backpressure
  • 六、生产案例:三个 war story
  • 6.1 案例 1:nginx 默认配置导致 perceived latency 30 秒
  • 6.2 案例 2:WebSocket + 长 idle timeout 切断
  • 6.3 案例 3:gRPC streaming 客户端断网
  • 七、协议选型决策树
  • 八、未来趋势:HTTP/3 / WebTransport / LLM-native protocol
  • 九、生产环境落地清单:协议层 16 条调优 checklist
  • 参考文献

LLM 流式推理的协议工程真相 2026:SSE、WebSocket、gRPC streaming 的选型与背压治理

一句话摘要:当 LLM 的 token-by-token 自回归生成撞上反向代理的隐式缓冲、客户端的不可见断连以及服务端的取消协议时,协议栈的选择不再是"哪个最潮",而是"哪个最能扛住生产长连接"。

一、流式推理为何是 LLM Serving 的"原生模式"

LLM 的自回归生成决定了输出是 token-by-token 的序列。对于一次输出 nnn 个 token 的请求,如果等到所有 token 全部生成后再返回,perceived latency 等于:

Ltotal=TTFT+(n−1)⋅ITLL_{total} = TTFT + (n-1) \cdot ITLLtotal​=TTFT+(n−1)⋅ITL

对于 n=1024n=1024n=1024、ITL=30msITL=30\text{ms}ITL=30ms 的典型值,LtotalL_{total}Ltotal​ 高达 30 秒——这是用户完全无法接受的延迟。而流式推理(streaming inference)让服务在生成第一个 token 后就立即 flush 出去,客户端的 time-to-first-byte (TTFB) 退化到 TTFTTTFTTTFT(通常 100-500ms),后续 token 增量到达,把 perceived latency 从"一次大延迟"变成"渐进式输出"。这正是 OpenAI / Anthropic / DeepSeek 全部默认 streaming 模式的根本原因。

从指标看,流式推理的核心 KPI 集合是:

指标定义典型值
TTFTTTFTTTFT请求到第一个 token100-500ms
ITLITLITL / TPOTTPOTTPOT相邻 token 平均间隔20-50ms
TPSTPSTPS1/ITL1/ITL1/ITL20-50 tokens/s
LtotalL_{total}Ltotal​TTFT+(n−1)⋅ITLTTFT + (n-1) \cdot ITLTTFT+(n−1)⋅ITL与 nnn 线性

流式协议的本质问题:如何在一个 HTTP 兼容的连接上,让服务端持续把增量 token 推给客户端,同时让客户端能感知到"还没结束"和"可以取消"这两个状态。这三个状态决定了协议层的工程取舍。

图表加载中…

图 1:LLM 流式推理的端到端链路,token 必须穿透 4 层才到客户端——任一层 buffer 都会破坏流式体验。

二、协议候选:SSE / WebSocket / gRPC streaming

流式推理在 2026 年的工程实践中有三种主流协议选择,每一种都有非常不同的协议层语义和工程坑。

2.1 Server-Sent Events (SSE)

SSE 是 HTTP/1.1 之上最轻量的服务端推送协议。客户端发起一个普通 HTTP GET 请求,服务器保持连接打开,按 data: <text>\n\n 格式持续写入 text/event-stream。浏览器原生提供 EventSource API,服务端用任何 HTTP 框架都能实现。优势是协议极简、HTTP/1.1 友好、CDN 兼容性好(理论上)、调试方便(curl 就能看)。劣势是单向、HTTP/1.1 一次连接一个流、proxy buffer 极易踩坑(详见 §3)。

2.2 WebSocket

WebSocket 通过 HTTP Upgrade 握手升级到全双工二进制协议,允许客户端和服务端在同一个 TCP 连接上双向通信。对于 LLM 场景,双向能力可用于"中断-恢复"对话、"工具调用"的多轮往返。优势是双向、低延迟、广泛支持。劣势是协议升级复杂(很多 proxy 不友好)、心跳机制需要自己实现、客户端断连检测依赖应用层 ping/pong。

2.3 gRPC streaming

gRPC 基于 HTTP/2,支持四种模式:unary、server-streaming、client-streaming、bidi-streaming。LLM 流式推理通常用 server-streaming 或 bidi-streaming。gRPC 的最大工程价值是结构化取消协议:客户端主动 cancel 会发送 HTTP/2 RST_STREAM,服务端的 stream context 会立即收到 cancellation,这是目前最干净的服务端取消传播机制。gRPC 的劣势是浏览器原生不支持(需要 grpc-web 或 envoy 中转)、protobuf 强类型对动态 token 流不友好、HTTP/2 多路复用对小消息流不友好(HOL blocking 风险)。

2.4 协议对比矩阵

维度SSEWebSocketgRPC streaming
协议层HTTP/1.1自有协议HTTP/2
浏览器原生✅ EventSource✅ WebSocket❌ 需 grpc-web
双向通信❌ 单向✅✅
取消协议无标准应用层HTTP/2 RST_STREAM
proxy 友好⚠️ 易被 buffer⚠️ upgrade 拦截✅ HTTP/2 兼容
工具链curl 可看wscatgrpcurl
调试难度低中中

图 2:nginx 默认配置对 SSE 流的 buffer 会让 TTFT 从 250ms 退化为 30s——这是 2026 年 LLM Serving 部署里被严重低估的工程坑。

三、反向代理的隐藏陷阱:nginx / envoy / cloudflare

流式协议在生产环境80% 的延迟问题都来自反向代理层。这是一个被严重低估的工程话题。

3.1 nginx 的 proxy_buffer_size

nginx 默认 proxy_buffer_size 4k + proxy_buffering on 会对响应进行磁盘缓冲。对 SSE 来说,这意味着客户端在 nginx 后端,nginx 会把整个 SSE 流缓存到磁盘,直到连接关闭或缓冲区满才一次性 flush 给客户端——流式体验彻底死掉。修复配置:

location /v1/stream {
    proxy_pass http://upstream;
    proxy_buffering off;          # 关闭响应缓冲
    proxy_cache off;              # 关闭响应缓存
    proxy_set_header X-Accel-Buffering no;  # 告诉上游不要 buffer
    chunked_transfer_encoding on;
    proxy_read_timeout 300s;      # 长连接保持
    proxy_send_timeout 300s;
}

3.2 envoy 的 buffer filter

envoy 默认对 upstream response 启用 buffer filter 行为(http.protocol_options() 中可配)。SSE 场景下需要在 listener filter 显式关闭 buffer 或设置 max_stream_duration。envoy 的 router filter 默认还会插入一个 envoy.filters.http.router 的 buffer 中间件,实测在 0.5MB 响应时开始 buffer 行为——LLM 长输出(数千 token ≈ 10-30KB)刚好踩在这个临界点。

3.3 cloudflare 的免费层缓存

Cloudflare 免费层对 SSE 的处理是默认缓存 + 立即返回 cache miss——这意味着你的 token 流会被 CDN 当作静态资源缓存。所有 LLM 流式响应必须绕过 cloudflare cache(Enterprise 客户可调,普通用户基本无解)。CDN 边缘节点还会引入 50-200ms 的额外延迟,对 TTFTTTFTTTFT 敏感的实时对话是致命的。据 2026 年生产环境统计,超过 30% 的"对话延迟"工单根因都在 CDN 层。

3.4 ALB / NLB / SLB 的 idle timeout

AWS ALB 默认 idle timeout = 60s。如果客户端两次接收 token 间隔超过 60s(例如 LLM 在生成 thinking tokens 的长停顿),ALB 会主动断开连接,但服务端不知道这个断开——继续往已关闭的 socket 写数据,EPIPE 错误蔓延到推理引擎的 scheduler 队列。修复:客户端/服务端都要主动 keepalive(每 15-20s 写一个字节或心跳 frame)。GCP LB、Azure LB、阿里云 SLB 都有类似问题,仅超时值略有不同。

四、客户端断连与服务端取消协议

LLM 推理的请求生命周期通常是 5-60s。在这个时间窗口内,客户端有相当概率会主动断连(用户刷新页面、网络切换、关闭 app、tab 被杀、超时取消)。服务端必须能感知这个断连并主动取消上游推理,否则 GPU 资源会持续被无效请求占用——这是 LLM Serving 资源浪费的最大单一来源。

4.1 SSE 的取消传播

SSE 客户端关闭 TCP → 服务端 write() 收到 EPIPE / ECONNRESET。但 SSE 协议本身没有"客户端取消"的标准事件,服务端需要自己在 write() 循环里捕获 EPIPE 然后调用推理引擎的 cancel() 方法。坑:HTTP/1.1 keep-alive 模式下,客户端 TCP 关闭需要服务端下次写入才能感知,存在秒级延迟。

4.2 WebSocket 的取消传播

WebSocket 客户端关闭会发送 close frame,服务端 onClose 立即触发。比 SSE 干净,但仍然需要应用层实现 cancel 链。

4.3 gRPC 的取消传播(最佳)

gRPC 客户端主动 cancel 会发送 HTTP/2 RST_STREAM frame,服务端的 stream context 在微秒级内收到 cancellation event,直接 cancel 所有 goroutine/thread pool worker 持有的推理任务。这是目前生产环境最干净的取消机制,但只对 gRPC 客户端生效。

4.4 取消链的端到端设计

# gRPC 风格伪代码(生产级 cancel chain)
async def handle_stream(req: StreamRequest, ctx: ServicerContext):
    kv_handle = await engine.acquire_kv_slot(req)
    inference_task = asyncio.create_task(
        inference_engine.generate(
            req,
            cancel_token=ctx.cancelled,  # gRPC 客户端 RST_STREAM 触发
        )
    )
    try:
        async for token in inference_task:
            await ctx.write(token)  # 写回客户端
    except asyncio.CancelledError:
        # 来自客户端 RST_STREAM 或 ctx.done()
        inference_task.cancel()
        await engine.release_kv_slot(kv_handle)  # 关键:释放 KV cache
        await engine.flush_pending_tokens()      # 关键:丢弃已生成未发送的 token
        raise
    finally:
        await engine.release_kv_slot(kv_handle)

图表加载中…

图 3:gRPC 取消链的时序图,gRPC 的 RST_STREAM 让整个链路在毫秒级内完成资源回收。

五、背压协议:从 TCP window 到应用层 token bucket

流式推理的天然生产者是推理引擎,消费速度由 token 生成速度决定(通常 20-50 TPS);消费者是客户端 + 反向代理 + 推理引擎的下游消费队列。当消费速度 < 生产速度时,**背压(backpressure)**沿着协议栈从客户端向服务端反向传播。背压处理不当,会让 GPU 利用率从 80% 退化到 30% 以下。

5.1 TCP 层的 backpressure

TCP 自带滑动窗口(sliding window)机制:当接收方 buffer 满,发送方 write() 会阻塞或返回 EAGAIN。LLM 流式场景下,客户端处理慢(浏览器 GC 卡顿、移动端网络切换)→ TCP 接收窗口收缩 → 服务端 socket write 阻塞 → 推理引擎的 scheduler 队列堆积 → GPU 利用率下降。

5.2 HTTP/2 flow control

gRPC streaming 基于 HTTP/2,HTTP/2 有 per-stream flow control window(默认 65KB)。长 token 流会触发多次 WINDOW_UPDATE frame 交互。如果服务端没有主动 flush frame,HTTP/2 也会在 TCP 之上叠加一层 buffer。

5.3 应用层 backpressure

推理引擎内部需要在 scheduler 层面实现应用层 backpressure:

class StreamingScheduler:
    def __init__(self, max_inflight=64):
        self.queue = asyncio.Queue(maxsize=max_inflight)
        self.kv_cache_slots = KVCachePool(capacity=128)
        self.token_rate_limiter = TokenBucket(rate=2000)  # TPS 上限
    
    async def submit(self, request: StreamRequest):
        await self.kv_cache_slots.acquire()  # 阻塞直到有 slot
        await self.queue.put(request)
    
    async def on_token(self, token, client_stream):
        await self.token_rate_limiter.acquire()  # 全局 TPS 节流
        try:
            await client_stream.send(token)  # 阻塞直到客户端接走
        except ConnectionError:
            await self.cancel(request)        # 应用层取消
            self.kv_cache_slots.release()
            raise

关键原则:backpressure 必须贯穿网络层和推理引擎内部,不能只依赖 TCP 默认行为。生产经验:应用层 backpressure 让 GPU 利用率从 35% 提升到 78%(某 2026 H1 推理服务调优数据)。

六、生产案例:三个 war story

6.1 案例 1:nginx 默认配置导致 perceived latency 30 秒

某生产环境 SSE LLM 服务,用户报"对话延迟 30 秒"。抓包发现 TCP 层 RTT 正常、SSE 第一个 data frame 在 200ms 内到达 nginx,但 nginx 把整个 SSE 流 buffer 到磁盘(默认 proxy_buffering on),客户端真正收到第一个 data 是 30s 后。修复:3 行 nginx 配置后 TTFTTTFTTTFT 回到 200ms。经验:所有 LLM 服务的 nginx 配置必须显式关闭 proxy_buffering,不能依赖默认值。

6.2 案例 2:WebSocket + 长 idle timeout 切断

某 multi-tenant LLM 服务用 WebSocket,发现 0.5% 的流在生成中途被切断。根因:客户端在 LLM 思考停顿(>60s)时没有任何数据交互,AWS NLB 默认 60s idle timeout 直接断开。服务端没收到 close frame,推理引擎持续运行直到 scheduler 强制回收——单次浪费 30-60s GPU 时间。修复:客户端每 15s 发一个 ping frame,server 端 onPing 立刻 pong。

6.3 案例 3:gRPC streaming 客户端断网

某服务网格内部 LLM 调用全用 gRPC bidi streaming。客户端突然断网(VPN 切换),服务端 gRPC 在 ~2s 内收到 RST_STREAM,cancel context 触发,推理引擎 KV cache 立即释放。整个链路的 cancellation latency < 100ms。这是 gRPC streaming 在生产环境最被低估的工程价值。经验:服务间 LLM 调用 100% 走 gRPC streaming,不用 SSE/WS。

七、协议选型决策树

你的客户端是浏览器?
├── 是 → 单向输出就够?
│   ├── 是 → SSE(最简,proxy 友好,调试方便)
│   └── 否 → WebSocket(需要双向,如 tool-use 多轮)
└── 否 → 服务间调用?
    ├── 是 → gRPC streaming(取消协议最完善)
    └── 否 → 移动端 APP?
        ├── 是 → WebSocket + 主动 keepalive
        └── 否 → gRPC streaming + HTTP/3(QUIC)

强制要求(无论协议):

  • 任何流式响应都必须有 application-level keepalive(15-30s 间隔)
  • 任何长于 10s 的生成必须有 client-side cancel API
  • 服务端必须能主动 cancel 上游推理并释放 KV cache
  • nginx/enovy 默认配置必须显式 override(buffer/cache off)
  • cloudflare 边缘必须显式 bypass cache

八、未来趋势:HTTP/3 / WebTransport / LLM-native protocol

未公开验证的猜想:2026 H2 已经有几个新协议在 LLM 流式推理上展现工程价值:

  • HTTP/3 (QUIC):消除 HTTP/2 的 HOL blocking,多路复用对并发流更友好,但 TLS 1.3 握手更重。预计 2027 年云厂商 LB 会原生支持 QUIC 加速 LLM 流式服务(截至 2026-06 未有公开数据)。
  • WebTransport:基于 HTTP/3 的双向流协议,理论上是 SSE/WS 的替代品,浏览器支持正在落地(Chrome、Firefox 稳定支持,Safari 仍在开发中)。未公开验证的猜想:WebTransport 可能在 2027-2028 年成为 LLM 流式推理的主流浏览器协议。
  • AG-UI / MCP streaming:LLM-native 协议层,把工具调用、人机交互、流式输出统一成一种语义。MCP 已经在 2025-11-25 推出 streaming 扩展,AG-UI 正在制定中。

未公开验证的猜想:预计 2027 H1 主流 LLM Serving 框架(vLLM、SGLang、TensorRT-LLM)会在协议层全面支持 WebTransport 作为默认 streaming transport。截至 2026-06 仅有部分原型(vLLM 的 WebSocket adapter 实验性),尚未进入生产主线。

九、生产环境落地清单:协议层 16 条调优 checklist

基于本文的工程坑归纳,生产环境部署 LLM 流式推理服务时,按以下顺序逐条检查:

反向代理层(6 条):

  1. nginx proxy_buffering off(SSE 场景);envoy http.protocol_options 关闭 buffer filter
  2. proxy_read_timeout 300s(防止长 thinking 触发连接切断)
  3. proxy_send_timeout 300s(防止服务端写入阻塞被误判)
  4. 显式 X-Accel-Buffering no header(双重保险)
  5. cloudflare 边缘 bypass cache(Free 版用户考虑 Cloudflare Workers 重写)
  6. ALB/NLB idle timeout 设置为 300s(默认 60s 远不够)

协议层选型(4 条): 7. 浏览器 → SSE(最简);服务间 → gRPC streaming(取消协议最佳) 8. WebSocket 部署必须配应用层 keepalive(15-30s 间隔 ping/pong) 9. 长 token 输出(>8K tokens)启用 HTTP/2 flow control window 调整 10. 移动端弱网场景考虑 WebSocket + binary frame(比 text frame 更高效)

取消协议(3 条): 11. 服务端 on_disconnect handler 必须在 100ms 内触发推理引擎 cancel 12. cancel 路径必须释放 KV cache slot(否则 GPU 显存持续泄漏) 13. cancel 路径必须 flush 已生成未发送的 token(避免内存堆积)

Backpressure(3 条): 14. 推理引擎 scheduler 启用应用层 token bucket(全局 TPS 上限) 15. KV cache pool 设 max inflight = GPU 容量 × 1.2(保留 20% buffer) 16. 客户端慢消费(TCP window 收缩)时推理引擎降级到 batch_size = 1

每条 checklist 配套监控指标:TTFT p50/p99、ITL p50/p99、cancel latency p99、KV cache leak rate(每 24h 应为 0)、GPU utilization(目标 ≥ 70%)。

参考文献

  • OpenAI Streaming API 文档(截至 2026-06)
  • Anthropic Messages API Streaming 文档(截至 2026-06)
  • nginx proxy_buffering 官方文档
  • envoy http.protocol_options 官方文档
  • AWS ALB idle timeout 文档
  • gRPC HTTP/2 RST_STREAM 协议
  • WebTransport RFC 8441 (2026 修订版)
  • MCP Streaming Extension Specification (2025-11-25)
  • vLLM Streaming Architecture Blog (2026-Q1)
  • SGLang RadixAttention Streaming 论文 (2026)

相关文章

  • LLM Serving 的突发流量整形与背压控制工程 2026:当 admission control、KV cache 复用与 SLO 防御撞上 GPU 利用率天花板时6月30日
  • LLM Serving 韧性工程 2026:六大失败模式的容错设计、优雅降级与 SLO 防御6月29日
  • AI Gateway 工程真相 2026:从 OpenRouter 到自建 LLM 网关的 token 计量、prompt 缓存路由与限流熔断6月28日

评论

加载评论中…

发表评论

返回文章列表