本文是「Spring Boot AI 全栈后端」系列第 11 篇。前面 10 篇把能不能做讲透了这一篇讲最扎心的一句本地能跑不等于上线不挂。AI 接口比普通接口更娇气——贵、慢、还看第三方脸色。示例基于 Spring AI 2.0 / Boot 4.1。Spring Boot 把 AI 接口真正上线扛量限流 / 降级 / 可观测一、为什么 AI 接口特别容易上线即事故见过太多场景本地curl一下回答漂亮一上线流量进来三件事同时炸——被刷有人写了个脚本循环调你接口模型按 token 计费一晚上烧掉半个月预算。被慢模型本身 P99 两秒并发一高线程池占满连带把同进程里的普通接口也拖死。黑盒挂了不知道挂哪靠用户投诉才发现复盘时连失败了多少次都说不清。普通 CRUD 接口加个索引、加台机器就能扛AI 接口的瓶颈在它身后那个按量付费、还可能限流的模型服务。所以 V哥给 AI 接口上了四道防线层层降级绝不裸奔。二、第一道限流先把刷子挡在门外限流的意义很简单单实例扛多少 QPS明明白白写死超了直接 429让客户端退避重试。别让超额请求打到模型——那是最贵的一环。用令牌桶实现原子操作避免高并发超卖publicbooleantryAcquire(){refill();// 先看时间补桶longcurrent;do{currenttokens.get();if(current0)returnfalse;// 桶空了拒绝}while(!tokens.compareAndSet(current,current-1));returntrue;}接口层把桶空翻译成 429前端收到后能退避而不是无脑重发if(!limiter.tryAcquire()){metrics.rateLimited();thrownewRateLimitedException(请求过于频繁请稍后重试);}多实例部署时单机令牌桶不够用要换 Redis Lua 令牌桶但算法骨架一模一样这里先用内存版把逻辑跑通。三、第二道缓存别让同一个问题被问一万次你们退货政策是什么这种高频问题一天能被问几千次。每次都调模型既烧钱又慢。加一层带 TTL 的响应缓存相同问题直接返缓存。publicStringget(Stringkey){Entryestore.get(key);if(enull)returnnull;if(nowNanos.getAsLong()e.expireAt()){// TTL 过期store.remove(key);returnnull;}returne.value;}关键是TTL 必须带——政策会变不能把旧答案缓存到天荒地老。V哥把nowNanos做成可注入离线测试里手动拨时间不用真睡几秒就能验过期。四、第三道熔断后端真挂了别让它被活活打死限流管请求太多熔断管后端已挂。模型服务抽风时如果你还拼命重试等于往一个已经倒地的同伴身上踩。熔断器的逻辑连续失败到阈值就 OPENOPEN 期间所有请求直接走降级连后端都不碰冷却后才给一次复活机会。publicTTrun(SupplierTcall,SupplierTfallback){if(stateOPEN未过冷却期)returnfallback.get();// 开门期直接降级try{Trcall.get();failures.set(0);// 一次成功清零避免历史失败一直压着returnr;}catch(Exceptionex){if(failures.incrementAndGet()threshold){stateOPEN;openedAtnow;}returnfallback.get();// 失败也走降级给前端一个能展示的底线答案}}降级不是失败是我用一条兜底文案告诉用户稍后再试比抛 500 体面得多。一次成功就把失败计数清零这个细节很重要否则历史上失败过会一直压着开关明明后端好了还放不开。五、第四道可观测出了问题你得看得见前三道是防这一道是看。AI 接口最怕黑盒——成功率掉了、降级多了、被限流了你却要等用户投诉才知道。用 Micrometer 计数器把这几个数全埋上publicvoidrequest(){registry.counter(ai.requests).increment();}publicvoidsuccess(){registry.counter(ai.success).increment();}publicvoiderror(){registry.counter(ai.error).increment();}publicvoidfallback(){registry.counter(ai.fallback).increment();}publicvoidcacheHit(){registry.counter(ai.cache.hit).increment();}publicvoidrateLimited(){registry.counter(ai.rate.limited).increment();}接上 Spring Boot Actuator 的/actuator/prometheus再配 OpenTelemetry 导出Grafana 里就能看请求量、成功率、降级率、缓存命中率、被限流次数五条曲线。本地能跑和上线不挂之间差的就是出问题你看得见、调得动。六、四道防线的顺序是踩坑踩出来的把这四层串进一个ProductionAiService顺序是固定的不能乱请求 → 1.限流(挡刷子) → 2.缓存(省重复) → 3.熔断(护后端) → 4.调模型 → 指标先限流超量的根本不进后续再缓存高频命中直接返回再熔断后端挂了不硬刚最后才真正打模型。每一层都单独可测、互不耦合——这正是 V哥写代码的原则拆得开才能验得准。离线验证里我用一个桩把模型替掉分别断言桶空返回 429、缓存命中后模型只被打一次、后端失败后走降级且指标 1。七、一个真实翻车现场重试把后端踩死了讲个真事。有个团队上线 AI 客服没熔断只加了失败重试三次。某天模型服务抖动RT 从 2 秒飙到 20 秒超时触发重试——重试又超时、又重试。结果每一次用户请求在后端裂变成 3 次慢调用线程池 30 秒占满不但 AI 接口挂了同进程的健康检查、订单查询全被拖死整站雪崩。根因就一句话重试是赌它能好熔断是确认它没好就先撤。重试必须配熔断一起用否则重试本身就是放大器。V哥的规则是——重试最多 1 次且只在熔断 CLOSED 态才允许OPEN 态直接降级绝不重试。还有个隐藏坑熔断 OPEN 后很多人 forgetting 把冷却期设得跟他心跳一样短。设 1 秒等于后端刚想喘口气又被一脚踩上设 30 秒给后端足够的缓冲。V哥默认 30 秒起步。八、上线前 V哥的自查清单限流容量配了吗单实例 QPS 写死超了返 429绝不裸奔到模型。高频回答缓存了吗带 TTL政策类别缓存太久。熔断阈值设了吗后端抽风时自动降级不把后端打死。指标暴露了吗Actuator Prometheus Grafana失败率掉了一眼能看到。降级文案体面吗不能抛 500 堆栈给用户给一句稍后再试。离线先跑绿令牌桶、熔断、缓存、指标逐层断言再写进文章。九、参数收进配置别写死在代码里上一条铁律能调的参数绝不写死。限流容量、熔断阈值、缓存 TTL都应该跟着环境走——压测环境放宽生产环境收紧。在application.yml里统一收口再用Value注入ai:ratelimit:capacity:200# 单实例令牌桶容量refill-per-second:200# 每秒补 200 个令牌circuit:failure-threshold:5# 连续 5 次失败熔断cooldown-seconds:30# 冷却 30 秒cache:ttl-minutes:10# 答案缓存 10 分钟ProductionAiService的构造函数改成读配置而不是写死 100/5/10publicProductionAiService(ChatModelchatModel,MeterRegistryregistry,Value(${ai.ratelimit.capacity})longcapacity,Value(${ai.ratelimit.refill-per-second})longrefill,Value(${ai.circuit.failure-threshold})intthreshold,Value(${ai.circuit.cooldown-seconds})longcooldown,Value(${ai.cache.ttl-minutes})longttl){this.limiternewTokenBucketRateLimiter(capacity,Duration.ofSeconds(1),refill);this.breakernewAiCircuitBreaker(threshold,Duration.ofSeconds(cooldown));this.cachenewResponseCache(Duration.ofMinutes(ttl),System::nanoTime);this.metricsnewAiMetrics(registry);}这样运维改个 yml 就能调容量不用改代码重新发版——V哥的经验是凡是上线后大概率要调的旋钮都从配置里长出来。十、让指标真正流出去光有计数器不够得让它流到监控里。Actuator 默认就把 Micrometer 的计数器暴露成 Prometheus 抓取格式加一行配置就能用management:endpoints:web:exposure:include:health,info,prometheus,metrics/actuator/prometheus里就能看到ai_requests_total、ai_fallback_total这些序列Grafana 拖个面板命中率掉、降级涨、被限流突增一眼可见。这一步才是可观测的闭环——前面埋的计数器到这里才变成你能盯着的曲线。四道防线和一份自查清单把本地能跑和上线不挂之间的鸿沟填平了。下一篇聊一个更底层的硬活老项目怎么从 Spring Boot 3.x 迁到 4.x AI 2.0——Jakarta 11、Jackson 3、包名漂移这些破坏性变更怎么一步步稳过。