推理服务接入限流保护,核心就一句话:在入口处就把超出承载能力的请求挡在门外,让系统永远运行在安全水位,宁可拒绝、不可拖垮。这听起来像废话,但绝大多数AI推理服务被突发流量打垮,恰恰是因为想着“多扛一点是一点”,最后连存量用户都服务不了。
为什么大模型推理服务比普通Web服务更怕突发流量
普通接口被流量冲垮,重启一下基本能恢复,推理服务完全不是这个逻辑。
推理服务是一套有状态的昂贵流水线
把推理服务拟人化一下:它就像一间只有一个大厨的火锅店,每来一桌客人,大厨要按顺序切菜、配锅、上菜,一桌没吃完,下一桌只能等着,你不可能像快餐店那样提前把几百份套餐煎好放着,推理请求必须实时算,而且每秒钟都在烧GPU的钱。
正因为如此,推理服务面对突发流量时有个致命特征排队越深,系统越慢,超时重试又让队列加倍拥堵,业内专家指出,大部分线上推理事故的起因不是请求量本身有多大,而是超时重试机制形成“雪球效应”,把网关和推理实例之间的连接池、线程池全部占满,最终连健康检查都无响应。
突发流量经常在以意想不到的姿势出现
写文章的人常说“高并发场景”,真实世界里高并发怎么来的?
- 早上通勤高峰,某个城市的“早报摘要”App同时推送,几万人同时刷同一款模型的新闻摘要接口
- 运营晚上偷偷上线了一个抽奖活动,H5页面在朋友圈裂变,一分钟内打进来10倍于平时的请求
- 你合作的上游渠道做了个“AI助手免费体验”的投放,短视频带量,流量是陡坡式拉满,不是爬坡式渐增
这几种场景的共同点是:请求的波形是峭壁状的,而自动扩容的节奏根本追不上,等你把新的GPU实例拉起来,旧实例早已经被队列压垮。
推理服务限流方案选哪个:从网关到实例的四层防线
网上搜“大模型推理服务限流方案”,出来的文章大多在讲令牌桶和漏桶的区别,但真实的百度智能云、简米云生产环境里,没人只靠一个算法打天下,行业共识认为,推理服务限流必须做分层,每一层管不同的“兵种”。
第一层:入口网关限流,拦住大头
入口网关(比如Nginx、Kong、API网关)是第一道防线,这一层只需做粗粒度限流按API Key、按来源IP、按用户维度设配额,别在这一层做深度语义判断,别去看请求体里的Prompt长度,那会拖慢转发速度。
实操上,用Nginx的limit_req_zone做基础限流非常直接:
limit_req_zone $binary_remote_addr zone=ai_limit:10m rate=20r/s; location /v1/completions { limit_req zone=ai_limit burst=40 nodelay; proxy_pass http://inference_backend; }
关键参数是burst,突发流量来时,burst允许短时间消化40个额外请求,但超过这个量立刻拒绝并返回429。nodelay保证排队延迟不累积。
第二层:服务端信号量限流,护住线程池
网关拦不住的那部分请求,会落到推理服务进程里,对大模型推理来说,最大的风险不是CPU跑满,而是线程池被打满,PyTorch的推理进程默认有多线程并行机制,每个并发请求都会占用显存和计算资源。
这里用信号量(Semaphore)做服务端并发数控制比计数器更合适:
- 计数器管“每秒多少个请求”
- 信号量管“同一时刻最多多少个请求在跑”
如果你们的推理服务用Python FastAPI写,可以在依赖注入里装一个asyncio.Semaphore(16),这个16怎么定?取决于你的GPU型号和模型尺寸,比如一张A100跑7B模型,同时处理8个请求时延迟已经明显上升,那并发上限就压到6,留出20%余量。
第三层:队列长度限制,防止“排队即雪崩”
这一层最容易被忽略,推理服务有自己的任务队列,当并发请求超过处理能力,多余的请求会排队,队列设计得好,像机场安检的蛇形通道,虽慢但有序,设计得不好,就像早高峰的地铁闸机,所有人塞在一个狭窄通道里。
实操上做两件事:
- 给队列设硬上限,比如最多积压50个请求,超过的直接返回“系统繁忙”
- 队列里的请求设最大等待时间(比如5秒),超时立即拒绝,不让客户端无限期挂着
第四层:实例级背压与熔断,保护单台机器
到了实例级,限流就变成了自我保护,每个推理实例要监听自己的GPU显存使用率和平均延迟,当显存占用超过90%或者P99延迟超过某个阈值(比如3秒),实例主动向负载均衡器报告“我病了,别再派流量过来了”。
主流做法是用Sentinel或Resilience4j做熔断器,熔断器有三种状态:关闭、打开、半开,连续失败超过阈值就打开熔断,快速失败所有请求;过一会儿进入半开状态放几个探针请求试试水深。
百度智能云推理服务限流怎么配置:以实际控制台为例
如果你直接用的是百度智能云的模型服务平台的推理服务,不需要自己从零写限流代码,控制台里就带配额管理能力,路径大致是:进入控制台 → 找到目标推理服务 → 配额管理 → 配置QPS上限和并发度。
这里有个容易踩的坑:控制台上的默认QPS看起来很高,但那是平台侧的网络转发能力,不是你的模型真正能撑住的推理能力,很多人困惑“为什么我的服务在平台上配置了100 QPS,一到高峰期还是卡顿”,因为在平台之后,你的模型推理实例可能只有2个副本,控制台上配置的QPS只是入口,真正的瓶颈在显存计算。

常用的配置思路是:
- 先压测出自己的单副本真实推理能力(详见后文)
- 用单副本能力乘以副本数量,得到总吞吐基线
- 在平台上把QPS设为总吞吐基线的一半左右,给下游存储和网络留出足够缓冲
- 多副本之间用“均匀分布”策略,不要用“性能优先”策略,后者容易把流量倾斜到最空闲的实例上
接口限流搭好了,还要配合降级策略
限流本身不是目的,让用户拿到合理响应才是目的,把请求挡在门外之后,所谓“合理响应”有两种方式:
优先级队列比一刀切更聪明
不是所有请求都该平等对待,把请求分成两类:
- 交互类请求(用户在网页/App里等结果):优先放行,延迟要求高
- 批处理类请求(离线跑数据、文档批量总结):可以在队列里多等一会儿
实现方式是用两个队列,权重分配4:1,当高峰期容量不够时,批处理请求的拿票率自动降到最低,交互类请求的拿票率保持满额,这样即使用户最多,核心体验也不会崩。
快速失败加有温度的提示
突发流量时,很多服务直接返回一个光秃秃的“503 Service Unavailable”,这个体验太冰冷,而且用户不知道什么时候该重试。
更好的降级响应是返回带重试信息的结构化JSON:
{
"code": 429,
"message": "请求量过大,排队中,请在3秒后重试",
"retry_after": 3
}
客户端拿到这个响应就知道该等多久,这比用户自制乱刷要好得多,因为乱刷会导致重试风暴。
限流参数怎么调:用压测数据说话
最忌讳的是拍脑袋定限流值,某团队拍了个50 QPS的上限,结果高峰期真实流量刚好卡在边缘,用户体验反而比不限流更差因为总是差那么一口气。
压测工具选择
对推理服务压测,别用JMeter那套图形界面工具,上手慢且脚本维护成本高,用wrk或者ghz(针对gRPC接口),几行命令就能模拟高并发:
wrk -t8 -c100 -d60s -s post.lua http://your-inference-endpoint/v1/completions
c100是模拟100个并发连接,-d60s是持续压1分钟,压测时重点观察三个指标:
- P50、P95、P99延迟
- 错误率(5xx和超时)
- GPU的利用率曲线
判断限流阈值的实战逻辑
先不被限速地压测,从低并发逐步往上加,当并发加到某个数字时,P99延迟开始直线上升而不是平缓增长,那个拐点就是系统的“膝盖”,限流阈值要设置在膝盖的75%位置。

举例:某个模型服务在12个并发时P99延迟还稳定在800ms,到16个并发时P99突然跳到2500ms,那么阈值应该设在9,因为流量是波动的,如果极限压到12,稍微有一点波动就会越界。健康运行的推理服务,资源利用率永远不该跑在极限。
限流也要做容量规划:没有哪套限流参数是一劳永逸的
限流参数设定了不代表就完事了,推理服务的承载能力会随着上线模型的变化而变化:
- 今天跑的是7B模型,明天换了70B模型,吞吐量直接打个对折
- 一个业务上线了长上下文版本,请求的平均Token数从500涨到2000,同样的并发数,GPU显存消耗翻倍
因此每次模型上线后,都要重新压测一遍、重新校准限流阈值,同时关注扩展副本后限流参数要不要同步放大,有人用网关上的限流配置做了动态化改造,在K8s里注册了一个根据副本数自动调整QPS的Sidecar,每次副本变动时自动拉取最新限流值。
这个闭环是:压测得出单副本承载能力 → 运维平台记录当前副本数 → 计算平台自动算出总QPS阈值 → 推送到网关内存,整个链路全自动,人只负责审核压测报告。
推理服务限流怎么做:常见问题解析
限流选在API网关层还是服务层?
两层都需要,网关层负责挡住显然不合规、来源可疑的大流量,比如单个IP每秒打5000个请求,这种根本不该进入服务层判断,服务层负责精细化管理按用户等级、按请求类型分配不同的配额,只做网关层限流,容易让同一用户的并发请求全部进入实际计算队列,造成资源浪费。
令牌桶和漏桶算法,对推理服务哪个更友好?
令牌桶更合适,推理请求天然存在“突发性”,用户在想问题的时候是空闲的,想到之后会快速连续发好几个请求,令牌桶允许一定程度的突发,漏桶把输出磨平了,虽然对下游存储最友好,但对用户体验反而造成不必要的等待,其实生产环境中治理突发流量还有一种常用变形叫“滑动窗口”,它比令牌桶更容易控制固定时间窗口内的总量,但实现复杂度稍高,适合有专门基础架构团队的场景。
限流返回429之后,客户端应该怎么处理?
客户端收到429时,应优先读取响应头里的Retry-After字段,按指定时间退避,很多AI应用接入推理服务之后,框架默认的重试机制是等1秒就重试一次,这种策略几乎必然导致输赢“追尾”,建议用指数退避:第一次等2秒、第二次等4秒、第三次等8秒,最多重试3次就放弃,把控制权还给用户体验逻辑,提示用户稍后再试。
