id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id
title
type
domain
stack
tags
level
prerequisites
est_hours
related
week
devops.nginx.scenario.ai-gateway-sse-canary
AI 应用网关:流式输出不卡顿、按用户灰度新版本
scenario
devops
intermediate
devops.nginx.https-stream-limit
2
devops.nginx.interview
devops.docker.scenario.compose-dev-env
AI 应用网关:流式输出不卡顿、按用户灰度新版本
业务背景与问题
企业内部的 AI 问答助手:前端单页应用,后端 Python FastAPI 调用大模型并以 SSE 流式返回。上线后遇到三个问题:
经过 Nginx 后,回答不再逐字出现,而是等十几秒后整段出现;长回答偶尔 504。
新版本的检索策略想先给内部 20 个同事试用,没有灰度手段,只能全量发布。
有人写脚本刷接口,模型调用费用突增。
约束
只有一台入口服务器上的 Nginx,不引入新的网关产品。
灰度要能按用户指定,也要能随时关闭。
正常用户的体验不能受限流影响。
方案设计
流式 : /ai/stream 单独一个 location, proxy_buffering off、proxy_cache off、proxy_read_timeout 300s;后端每 15 秒发一条 : ping 心跳,防止中间设备断开空闲连接。
灰度 : map $cookie_canary $api_backend,内部同事访问一个“加入体验”页面,由后端为其设置 canary=1 的 Cookie;关闭灰度只需把 map 默认值都指向稳定版并 reload。也可以用 split_clients 按比例放量。
限流 :Nginx 按 IP 做第一道粗粒度限流(limit_req,返回 429);后端再按用户 / 租户做精细的配额(Redis,见 Redis 限流 ),并记录每个用户的模型调用量。
关键代码
完整配置模板和自动化测试见 examples/gateway :
FastAPI 端的 SSE 响应头和心跳(示意):
测试用真实 Nginx 验证了四点:SPA 回退、反向代理并传递 X-Forwarded-For、带 canary=1 Cookie 的请求进入灰度后端、SSE 第一条事件不被缓冲、超过限流返回 429。
取舍
方案
优点
缺点
Nginx map / split_clients(本方案)
无新组件,配置简单
规则复杂时难维护;改规则要 reload
API 网关产品(APISIX、Kong 等)
动态配置、插件丰富
多一套系统要运维
应用内特性开关
粒度最细(按功能)
需要改代码,新旧逻辑并存
Kubernetes + 服务网格
按权重、按头部灰度都很方便
需要 K8s 和网格基础设施
易错点
只在 /api/ 关了缓冲,SSE 走的其实是另一个 location。
开了 gzip 且把 text/event-stream 加进 gzip_types,流式又被攒起来。
proxy_read_timeout 理解成总时长:它是两次读之间的最大间隔,心跳能避免超时。
灰度 Cookie 由前端随意设置:任何人都能进灰度。应由后端校验身份后设置。
限流只在 Nginx 按 IP:公司出口是同一个 IP,所有同事一起被限。内部系统应按用户维度在后端限流。
面试怎么讲
“AI 助手经过 Nginx 后流式输出变成整段,原因是 Nginx 默认缓冲代理响应。我给流式接口单独配了 location,关闭缓冲、放宽读超时,后端加心跳;用 map 按 Cookie 把内部同事路由到灰度版本;在 Nginx 做按 IP 的粗限流、后端按用户做配额。我还写了自动化测试,用真实 Nginx 验证第一条事件不被缓冲、灰度和限流生效。”