当外部服务宕机、或者遭遇恶意刷接口时如何保证你的 AI 应用不崩溃01 引子在上一篇中我们通过虚拟线程和模型路由解决了“高并发卡顿”和“成本失控”的问题。但真实的生产环境远比你想象的险恶遭遇恶意攻击有人写个脚本一秒内用同一个账号狂刷 100 次你的 AI 接口你的 Token 账单瞬间爆炸。云服务不稳定阿里云百炼的 API 偶尔也会抖动返回429 Too Many Requests或者503 Service Unavailable。如果你的代码直接抛异常用户看到的将是冷冰冰的“系统错误”页面。级联雪崩如果一个请求卡死导致线程池被占满整个应用都会瘫痪。这一篇的目标引入“限流”和“降级”两道防线让你的 AI 应用具备生产级的抗压能力。02 核心概念限流与降级机制目的手段限流控制请求速率保护系统不被打垮令牌桶算法Guava RateLimiter降级外部服务不可用时提供有损但友好的服务捕获异常返回兜底话术或备用数据03 代码实现构建双层防御3.1 第一步引入 Guava在pom.xml中引入轻量级的限流工具dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId version33.0.0-jre/version /dependency3.2 第二步实现本地限流器创建一个RateLimitService。需要注意的是由于阿里云的配额通常是基于 API Key 全局生效的所以我们应该做全局限流而不是按用户单机限流。package com.example.myai.service; import com.google.common.util.concurrent.RateLimiter; import org.springframework.stereotype.Service; Service public class RateLimitService { // 全局单例限流器设定每秒允许处理 2 个请求根据你的配额调整 private final RateLimiter globalLimiter RateLimiter.create(2.0); /** * 尝试获取令牌 * return true放行false被拦截 */ public boolean tryAcquire() { // tryAcquire() 是非阻塞的拿不到令牌立刻返回 false return globalLimiter.tryAcquire(); } }3.3 第三步实现降级兜底服务创建一个FallbackService负责识别错误类型并返回友好的提示语。package com.example.myai.service; import org.springframework.stereotype.Service; Service public class FallbackService { public String getFallbackMessage(String type) { switch (type) { case RATE_LIMIT: return 系统当前访问人数过多请稍后再试。; case API_ERROR: return AI 服务暂时不可用请稍后重试。; default: return 抱歉服务出现了点问题请稍后重试。; } } /** * 判断异常是否需要触发降级 */ public boolean shouldFallback(Exception e) { if (e null || e.getMessage() null) return true; String msg e.getMessage().toLowerCase(); // 识别超时、限流(429)、服务端错误(503)等 return msg.contains(timeout) || msg.contains(429) || msg.contains(ratequota) || msg.contains(connection) || msg.contains(503); } }3.4 第四步改造 AIService核心将限流前置拦截和降级后置兜底整合进AIServicepackage com.example.myai.service; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.stereotype.Service; Service public class AIService { private final ChatClient chatClient; private final AICacheService cacheService; private final ModelRoutingService routingService; private final RateLimitService rateLimitService; private final FallbackService fallbackService; public AIService(ChatClient.Builder builder, ChatMemory chatMemory, AICacheService cacheService, ModelRoutingService routingService, RateLimitService rateLimitService, FallbackService fallbackService) { this.cacheService cacheService; this.routingService routingService; this.rateLimitService rateLimitService; this.fallbackService fallbackService; this.chatClient builder .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); } public String ask(String conversationId, String question) { // 1. 【限流防线】检查本地令牌桶 if (!rateLimitService.tryAcquire()) { System.out.println( 本地限流拦截: 拒绝请求); return fallbackService.getFallbackMessage(RATE_LIMIT); } // 2. 查缓存 String cachedAnswer cacheService.get(question); if (cachedAnswer ! null) return cachedAnswer; // 3. 路由选择模型 String model routingService.route(question); // 4. 【降级防线】调用大模型并捕获异常 try { String answer chatClient.prompt() .user(question) .options(ChatOptions.builder().model(model).build()) .advisors(advisor - advisor .param(chat_memory_conversation_id, conversationId)) .call() .content(); cacheService.put(question, answer); return answer; } catch (Exception e) { System.err.println(❌ 云端调用失败: e.getMessage()); // 判断是否触发降级 if (fallbackService.shouldFallback(e)) { return fallbackService.getFallbackMessage(API_ERROR); } return fallbackService.getFallbackMessage(UNKNOWN); } } }04 实战测试见证 0ms 的拦截我们编写一个接口瞬间发起 5 次请求看看系统如何应对// RateLimitController 略核心逻辑是循环调用 aiService.ask() 5次实测返回的 JSON 结果经典的多层防御表现{ success: true, conversationId: test002, responses: [ {answer: AI 服务暂时不可用..., timeMs: 57}, {answer: AI 服务暂时不可用..., timeMs: 65}, {answer: AI 服务暂时不可用..., timeMs: 60}, {answer: 系统当前访问人数过多..., timeMs: 0}, {answer: 系统当前访问人数过多..., timeMs: 0} ] }结果深度剖析请求序号耗时拦截位置实际发生了什么1, 2, 3~60ms云端降级发出请求后被阿里云 429 拒绝FallbackService捕获异常并返回友好提示。4, 50ms本地限流连网络请求都没发出去RateLimiter直接拦截秒级返回提示。这就是生产级应用该有的样子外部服务崩溃了你的系统不崩溃有人恶意刷接口你既保护了系统又省了云端调用的钱。05 踩坑记录坑一本地限流与云端限流的冲突现象明明代码里没怎么拦截但一测试就报429 Too Many Requests。原因阿里云的 API Key 配额是全局的。如果你使用了多个不同的conversationId并发测试本地基于单用户的限流器不会拦截但云端会拦截。解决限流器必须做成全局单例如上文代码中的globalLimiter确保发往云端的 QPS 永远不超过云厂商的配额。坑二吞掉了真实的错误信息现象不管什么异常都返回“系统繁忙”导致排错困难。解决在catch块中一定要把真实的异常信息打印到日志里System.err.println(❌ 云端调用失败: e.getMessage())但返回给用户的文字必须是脱敏的、友好的。06 成果总结性能优化阶段通关至此你的 AI 应用性能优化阶段圆满完成优化项解决痛点收益Redis 缓存重复提问慢且费钱响应降至毫秒级大幅省 Token虚拟线程IO 阻塞导致并发能力差轻松支撑数万级并发模型路由所有问题都用大模型导致成本高综合成本直降 50%-70%限流与降级恶意攻击、云服务宕机系统高可用优雅兜底防止费用失控07 下期预告你的 AI 应用现在已经具备了完整的业务能力和生产级的抗压能力。但它现在还只能跑在你的本地 IDEA 里。如何把它打包成镜像部署到服务器上让全世界都能访问下一篇我们将进入 D 阶段部署与运维——用 Docker 将你的 AI 应用容器化。 我是超超不吵吵10年Java全栈正在转型AI应用开发。每周一篇实战笔记不贩卖焦虑只分享能落地的技术。全网同名欢迎关注。