首页/新闻资讯/正文详情

LLM高并发调用限流实战:从秒杀思维到并发闸门的架构演进

发布时间:2026/9/24 21:17:11 来源:云帆数科 栏目:资讯中心
LLM高并发调用限流实战:从秒杀思维到并发闸门的架构演进
1. 从一次线上事故说起为什么秒杀那套限流思路在 LLM 场景下会翻车去年年底我接手了一个 AI 客服中台项目底层对接了三家不同厂商的大模型服务上层是十几个业务方通过 HTTP 接口调用。上线第一周就出了事某个业务方做了一轮批量数据清洗瞬间打进来两千多个请求结果整个中台的响应时间从平均 1.8 秒飙到 40 秒以上所有业务方的调用全部超时连正常的在线客服对话都断了。当时我的第一反应是加限流毕竟做了这么多年后端秒杀场景下的 QPS 限流、令牌桶、漏桶、Sentinel 流控规则这些东西闭着眼睛都能写。于是我按照秒杀系统的经典套路在网关层给每个业务方配了 QPS 阈值超过就快速失败。结果呢问题只解决了一半——业务方的请求确实被拦住了但 LLM 调用本身的并发问题依然存在因为真正的瓶颈不在入口而在调用汇聚点。这个经历让我彻底想明白了一件事AI 接口的高并发和秒杀高并发本质上是两种完全不同的东西。秒杀的核心矛盾是瞬时流量洪峰 库存扣减的强一致性而 LLM 接口的核心矛盾是长耗时调用 上游配额有限 下游连接池脆弱。你把秒杀那套闸门挂在入口等于在洪水来的时候只堵住了上游河道下游的水库该溢还是溢。这篇文章我想把踩过的坑、试过的方案、最终落地的架构完整讲一遍。如果你正在做 AI 应用开发、LLM 中台、或者任何需要对接大模型 API 的后端系统尤其是被并发问题折磨过的同行这篇内容应该能帮你少走至少两个月的弯路。关键词里的 AI、LLM、并发闸门、QPS 限流、Sentinel 这些概念我会结合实际代码和配置逐个拆开讲。2. LLM 调用和秒杀请求的本质差异先搞清楚你在限什么2.1 耗时量级差了三个数量级限流模型必须换秒杀场景下一个下单请求的处理时间通常在 10 到 50 毫秒之间瓶颈在数据库行锁和库存扣减。而一次 LLM 调用哪怕是流式返回首 token 延迟普遍在 500 毫秒到 3 秒完整响应时间从 2 秒到 30 秒不等。如果是 GPT-4 这类大模型做复杂推理单次调用超过 60 秒都很正常。这个差异带来的直接后果是秒杀场景下 QPS 是有效指标LLM 场景下 QPS 几乎没意义。你限制每秒 100 个请求但如果每个请求要占用连接 10 秒那实际并发连接数就是 1000上游厂商的配额早就爆了。正确的指标应该是并发数Concurrency也就是同时处于已发出、未返回状态的请求数量。我后来在 Sentinel 里把流控模式从 QPS 改成了并发线程数控制效果立竿见影。具体配置后面会详细讲。2.2 上游配额是硬约束不是你能弹性扩容的秒杀系统的容量瓶颈在你自己的数据库理论上可以通过分库分表、缓存预热、队列削峰来扩容。但 LLM 接口的瓶颈在上游厂商的账户配额这个配额是合同写死的你没法临时扩容。我对接的三家厂商配额分别是A 厂商 60 并发、B 厂商 30 并发、C 厂商 20 并发。注意这是账户级的不是接口级。也就是说你所有业务方加起来对 A 厂商的同时调用不能超过 60 个。一旦超过要么被限流返回 429要么请求排队直到超时。这就意味着限流闸门必须挂在调用汇聚点——也就是所有业务方请求最终汇聚到某个厂商客户端的那一层。挂在网关层只能控制入口流量控制不了实际打到厂商的并发。2.3 连接池和线程池的脆弱性被严重低估秒杀场景下HTTP 连接池通常配置几百个连接请求快速进出连接复用率高。但 LLM 调用是长连接占用一个连接被占住 10 秒连接池很快就耗尽了。我踩过的最大的坑是用默认的 HttpClient 连接池配置最大 50 连接结果 60 个并发请求打进来第 51 个开始就阻塞在连接获取上然后 Tomcat 线程池被拖垮整个服务雪崩。后来我把连接池按厂商配额精确配置并且加了连接获取超时才稳住。下面这张表是我总结的两种场景的核心差异建议你对照自己的系统检查一遍维度秒杀高并发LLM 接口高并发单请求耗时10-50ms2-60s核心限流指标QPS并发数瓶颈位置自身数据库上游厂商配额扩容方式分库分表、缓存无法弹性扩容连接池压力低快速复用高长占用失败重试代价低高浪费配额限流闸门位置网关入口调用汇聚点3. 并发闸门到底该挂在哪三种方案的实测对比3.1 方案一网关层限流我最初的选择也是最先被淘汰的最直觉的做法是在 Spring Cloud Gateway 里配 Sentinel 流控规则按业务方维度限 QPS。配置大概长这样spring: cloud: sentinel: datasource: flow: nacos: server-addr: localhost:8848 dataId: gateway-flow-rules rule-type: flow然后在 Sentinel 控制台给每个路由配 QPS 阈值。这个方案的问题在于它只能控制入口速率控制不了在途并发。业务方 A 每秒发 10 个请求每个请求耗时 10 秒那 A 的在途并发就是 100早就超过厂商配额了但网关层看 QPS 完全正常。我实测下来网关限流只能防住恶意刷接口这种场景对正常的业务高峰毫无办法。3.2 方案二业务层限流粒度太细维护成本爆炸第二个想法是在每个业务方的 Service 层加限流。每个业务方调用 LLM 之前先 acquire 一个信号量。这个方案能控制并发但问题是配额是账户级的业务方之间会互相挤占。业务方 A 占了 50 个并发业务方 B 就只剩 10 个但 B 可能正在做紧急的在线客服A 在做离线批处理优先级完全不同。而且业务方有十几个每个都要配一套限流逻辑维护成本极高。我试了两周就放弃了。3.3 方案三调用汇聚点限流最终落地方案最终我选择的方案是在所有业务方和厂商客户端之间加一层统一的 LLM 调用网关闸门挂在这一层。架构大概是业务方A ─┐ 业务方B ─┼─→ LLM调用网关 ─→ 厂商A客户端配额60 业务方C ─┤ ├→ 厂商B客户端配额30 ... ─┘ └→ 厂商C客户端配额20这一层的核心职责有三个按厂商维度做并发控制每个厂商一个独立的信号量或 Sentinel 资源阈值等于配额打个 8 折留缓冲。按业务方维度做优先级排队高优先级业务在线客服可以抢占低优先级业务离线批处理的配额。统一做超时、重试、降级避免每个业务方自己实现一套。这个方案的好处是闸门位置和瓶颈位置对齐了。厂商配额是 60我就在汇聚点控制并发不超过 4880%这样既不会触发厂商限流又留了 20% 的弹性空间应对突发。4. 用 Sentinel 实现并发闸门的完整配置与代码4.1 Sentinel 并发线程数模式的正确用法Sentinel 的流控规则里有个grade参数0是 QPS1是并发线程数。LLM 场景必须用1。配置示例FlowRule rule new FlowRule(); rule.setResource(llm_vendor_a); rule.setGrade(RuleConstant.FLOW_GRADE_THREAD); // 并发线程数模式 rule.setCount(48); // 并发阈值 rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 快速失败 FlowRuleManager.loadRules(Collections.singletonList(rule));这里有个坑要注意并发线程数模式统计的是当前正在执行该资源的线程数所以你的 LLM 调用必须在 Sentinel 的SphU.entry()和entry.exit()之间执行否则统计不准。我见过有人把 entry 写在异步回调外面结果并发数永远是 1。正确的写法public String callLlm(String vendor, String prompt) { Entry entry null; try { entry SphU.entry(llm_ vendor); // 实际的 LLM 调用同步阻塞直到返回 return llmClient.invoke(prompt); } catch (BlockException e) { // 被限流走降级逻辑 throw new LlmThrottledException(vendor vendor 并发已满); } finally { if (entry ! null) { entry.exit(); } } }4.2 连接池配置必须和并发阈值对齐这是我最想强调的一点。Sentinel 的并发阈值是 48那你的 HTTP 连接池最大连接数必须大于等于 48否则请求会阻塞在连接获取上Sentinel 统计的并发数会虚高导致误限流。我用的 OkHttp 配置OkHttpClient client new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) // LLM 响应可能很慢 .writeTimeout(10, TimeUnit.SECONDS) .connectionPool(new ConnectionPool(60, 5, TimeUnit.MINUTES)) .build();注意readTimeout要设得足够大LLM 调用 60 秒很正常。但也不能无限大否则一个卡死的请求会一直占着连接。我的经验是设为 P99 响应时间的 1.5 倍。4.3 优先级排队的实现思路Sentinel 本身不直接支持优先级队列但可以通过多资源 动态规则来实现。思路是给每个业务方分配一个优先级高优先级的业务方在低优先级业务方被限流时可以借用配额。具体做法是给每个厂商资源配两个规则一个是保底配额所有业务方共享一个是弹性配额只对高优先级业务方开放。低优先级业务方只能用到保底配额高优先级业务方可以用到保底 弹性。// 保底规则所有业务方共享阈值 30 FlowRule baseRule new FlowRule(llm_vendor_a_base); baseRule.setGrade(RuleConstant.FLOW_GRADE_THREAD); baseRule.setCount(30); // 弹性规则只对高优先级业务方开放阈值 18 FlowRule elasticRule new FlowRule(llm_vendor_a_elastic); elasticRule.setGrade(RuleConstant.FLOW_GRADE_THREAD); elasticRule.setCount(18);高优先级业务方先尝试 acquire 弹性资源失败再 acquire 保底资源低优先级业务方只 acquire 保底资源。这样在高峰期低优先级业务方会被限流但高优先级业务方依然能拿到配额。5. 那些文档里不会写的踩坑记录5.1 流式返回的并发统计陷阱LLM 的流式返回SSE是个大坑。如果你用SphU.entry()包住整个流式过程那并发数统计是对的但线程会被占住直到流结束。如果你在流开始时就entry.exit()那并发数会严重低估因为实际连接还占着。我的做法是流式调用单独用一套并发控制不用 Sentinel 的线程数模式而是用Semaphore手动控制。因为流式调用的生命周期跨越了多个线程IO 线程和业务线程Sentinel 的线程模型不适用。private final Semaphore streamSemaphore new Semaphore(48); public void streamLlm(String prompt, ConsumerString onChunk) { if (!streamSemaphore.tryAcquire()) { throw new LlmThrottledException(流式并发已满); } try { llmClient.stream(prompt, onChunk); } finally { streamSemaphore.release(); } }5.2 重试会放大并发必须加熔断LLM 调用失败重试是常见需求但重试会瞬间放大并发。我遇到过厂商 A 短暂抖动返回了一批 500结果重试逻辑把并发从 48 瞬间打到 96直接把厂商 B 的配额也拖垮了因为降级逻辑切到了 B。解决方案是重试必须走独立的并发控制并且加熔断。我用 Sentinel 的熔断降级规则当某个厂商的异常比例超过 50% 时直接熔断 30 秒期间所有请求走降级逻辑返回缓存结果或友好提示不再重试。DegradeRule degradeRule new DegradeRule(llm_vendor_a); degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); degradeRule.setCount(0.5); // 异常比例 50% degradeRule.setTimeWindow(30); // 熔断 30 秒 degradeRule.setMinRequestAmount(10); // 至少 10 个请求才统计5.3 超时时间设置的经验值超时设置太短正常请求被误杀太长卡死请求占着连接不放。我的经验值是非流式调用P99 响应时间 × 1.5通常 30-60 秒流式调用首 token 超时 10 秒整体超时 120 秒连接获取超时2 秒超过说明连接池不够用这些值不是拍脑袋定的我是用了一个月的时间把每次调用的耗时打到 Prometheus然后看 P50、P95、P99 分位数再往上留 50% 的余量。5.4 配额打 8 折的缓冲逻辑厂商说配额是 60你千万别真的用到 60。因为厂商的统计口径和你的可能有偏差而且网络抖动、重试、流式调用都会让实际并发略高于你的统计值。我统一打 8 折60 的配额用 4830 的用 2420 的用 16。实测下来这样基本不会触发厂商的 429。6. 监控告警没有可观测性的限流就是盲人摸象6.1 必须监控的四个核心指标限流配好了不代表万事大吉你必须能实时看到系统的状态。我监控的四个核心指标是各厂商的实时并发数这是最关键的直接反映配额使用率。限流触发次数按业务方和厂商维度统计能看出谁在挤占配额。调用耗时分布P50、P95、P99用于动态调整超时和并发阈值。异常率和熔断状态及时发现厂商故障。这些指标我用 Micrometer 打到 Prometheus然后在 Grafana 上做了看板。并发数的采集要注意Sentinel 的ClusterNode里有curThreadNum可以直接读。ClusterNode node ClusterBuilderSlot.getClusterNode(llm_vendor_a); double currentConcurrency node.curThreadNum();6.2 告警阈值怎么定告警不能太敏感否则天天响也不能太迟钝否则出事才发现。我的设置是并发数超过阈值 80% 持续 1 分钟警告并发数超过阈值 95% 持续 30 秒严重限流触发次数 5 分钟内超过 100 次警告熔断触发立即严重告警这套阈值跑了大半年误报率很低该抓的问题基本都抓到了。6.3 动态调整规则的能力业务是变化的配额可能调整业务方可能增减。所以限流规则必须支持动态调整不能写死在代码里。我用 Nacos 做规则的数据源Sentinel 控制台改完规则直接推送到 Nacos所有实例实时生效不用重启。spring: cloud: sentinel: datasource: flow: nacos: server-addr: ${nacos.addr} dataId: llm-flow-rules groupId: SENTINEL_GROUP rule-type: flow degrade: nacos: server-addr: ${nacos.addr} dataId: llm-degrade-rules groupId: SENTINEL_GROUP rule-type: degrade7. 从秒杀思维切换到 LLM 思维几个认知上的转变做完这个项目我最大的收获不是技术方案而是认知上的转变。分享几个我觉得最重要的点。第一限流的位置比限流的算法重要得多。很多人一上来就纠结用令牌桶还是漏桶用 Sentinel 还是 Hystrix但其实最关键的问题是闸门挂在哪。挂错了位置算法再精妙也没用。第二并发数比 QPS 更接近本质。LLM 场景下QPS 是个误导性指标。你应该关心的是同时有多少个请求在途因为这直接对应上游配额和连接池占用。第三留缓冲比压榨性能更重要。秒杀场景下我们习惯把资源用到极致但 LLM 场景下留 20% 的缓冲是保命的。因为上游配额是硬约束一旦触发限流恢复代价很高。第四可观测性是限流的前提。没有监控的限流就是盲人摸象你不知道阈值该设多少也不知道限流有没有生效。先做监控再做限流。最后分享一个我实际用下来很稳的小技巧给每个厂商客户端加一个预热逻辑。服务刚启动时连接池是空的第一批请求会慢一些。我在启动后主动发几个轻量请求比如让模型返回一个固定字符串把连接池预热起来这样正式流量进来时不会有冷启动的抖动。这个技巧在秒杀场景下也常用但在 LLM 场景下效果更明显因为 LLM 的连接建立和 TLS 握手开销更大。

相关推荐

AI接口高并发≠秒杀高并发:LLM汇聚点并发限流实战
AI接口高并发≠秒杀高并发:LLM汇聚点并发限流实战

1. 从一次线上事故说起:为什么秒杀那套限流方案在 LLM 场景下会翻车去年年底我接手了一个 AI 应用的后端治理工作,系统本身不算复杂:一个对话式产品,前端把用户输入发到网关,网关做鉴权、路由,然后调用后端… · 2026/9/24 21:17:11

用Python+OpenCV实现自走棋游戏自动化:从环境搭建到状态机实战
用Python+OpenCV实现自走棋游戏自动化:从环境搭建到状态机实战

不常玩自走棋的人可能不理解,这种模式算得上三国杀里最耗时间的玩法之一,一局动辄二三十分钟,日常任务如果每天都要打几局,人坐在电脑前根本就干不了别的。“一将成名”又是其中比较冷门的分支,匹配慢、操作重复、还得… · 2026/9/24 21:17:11

巴菲特选股核心标准:如何评估企业无重大长期债务
巴菲特选股核心标准:如何评估企业无重大长期债务

1. 为什么债务是巴菲特衡量企业的第一把尺 做投资这些年,我听过太多人谈论巴菲特——有人学他的“护城河”,有人学他的“能力圈”,还有人天天盯着他的持仓变动。但真正把巴菲特的投资逻辑吃透之后,你会发现一个被他反复提及、却常… · 2026/9/24 21:17:11

Dopamine API 符号全景图:基于 all_symbols 索引的强化学习框架完整导航
Dopamine API 符号全景图:基于 all_symbols 索引的强化学习框架完整导航

Dopamine API 符号全景图:基于 all_symbols 索引的强化学习框架完整导航 【免费下载链接】dopamine Dopamine is a research framework for fast prototyping of reinforcement learning algorithms. 项目地址: https://gitcode.com/gh_mirrors/do/dopamine … · 2026/9/24 21:50:25

PointNet权重加载与微调全攻略:从state_dict到点云分类实践
PointNet权重加载与微调全攻略:从state_dict到点云分类实践

简介:PointNet模型权重资源包,面向点云处理与深度学习部署开发者,提供可直接用于点云分类、部分分割和语义分割的预训练权重。PointNet作为点云处理经典模型,可直接解析不规则三维点云数据。压缩包共21个文件,总大小17… · 2026/9/24 21:50:25

网站克隆完全指南:从静态镜像到CMS迁移的实战方法与合规边界
网站克隆完全指南:从静态镜像到CMS迁移的实战方法与合规边界

“网站克隆”这个词,我在实际项目里接触得非常多。过去几年帮朋友、客户做站点迁移、模板改造、全站备份,几乎每次都会被问到同一个问题:哪些网站是可以克隆的?克隆的时候需要注意什么?每次我都要把前提、工具、坑讲一… · 2026/9/24 21:50:25

眼底图像三任务深度学习实战:血管分割、中心凹检测与DR分级
眼底图像三任务深度学习实战:血管分割、中心凹检测与DR分级

简介:这是一套基于Python深度学习的智能眼底影像分析源码项目,面向医学图像处理、计算机视觉方向的在校生与研发人员,涵盖中心凹检测定位、眼底血管分割和糖尿病视网膜病变分级三大核心任务,适合用于毕业设计、课程设计或科研复现… · 2026/9/24 21:50:25

安卓端PS5模拟器实测:从原理到踩坑全解析
安卓端PS5模拟器实测:从原理到踩坑全解析

模拟器这圈子最近算是憋了个大招,确切的说是安卓端PS5模拟器的消息终于不再停留在概念层面了。我刷开发者社区的时候,看到一个视频:一台手机屏幕上跑着PS5的系统界面,游戏还真进去了,虽然帧率看着有点让人血压高&#… · 2026/9/24 21:50:25

AI 分镜如何把文本脚本转换为可执行镜头
AI 分镜如何把文本脚本转换为可执行镜头

很多团队已经可以用大模型生成一段完整脚本,但真正进入拍摄或视频生成时,仍然要重新回答一组工程问题:这一句文案对应哪一个画面?镜头之间如何保持连续?哪些镜头需要实拍,哪些镜头可以交给图片或视频模型&a… · 2026/9/24 21:50:19

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码