将大模型推向生产环境的架构师,最先要破除的执念就是“假定大模型接口永远可用”。在传统微服务中,一个 RPC 接口可用性低于 99.99% 就会被视作事故;而无论是国际公有云巨头还是国内模型供应商,大模型服务在面临突发算力排队或集群故障时,偶发 503 Service Unavailable、429 Too Many Requests 或者响应时间拉长至 30 秒以上,在业界是司空见惯的常态。
如果你的 Spring Boot 应用在调用大模型时缺乏健壮的防御体系,一个外部模型的抖动,会在两分钟内迅速耗尽你系统内的所有 Web 容器线程,引发全站级联雪崩。
构建一套涵盖重试退避(Retry Backoff)、熔断隔离(Circuit Breaker)与多级降级兜底(Fallback)的全立体容灾体系,是企业级 Spring AI 应用上线的生命线。
容灾分层架构设计
在设计 Spring AI 的防御链路时,我们将其划分为三道纵深防线:
- 第一道防线:自适应指数退避重试(Retry with Jitter):针对偶发的网络抖动(如连接重置
Connection reset)或供应商短时 429 限流,在网关层发起 1~2 次带随机抖动的轻量重试; - 第二道防线:基于错误率与慢调用的断路器(Circuit Breaker):利用 Resilience4j 对大模型调用进行滑动窗口统计。当最近 20 次请求中异常率超过 50%,或者 P95 耗时突破 8 秒时,断路器直接开启(OPEN),拦截后续所有请求,避免业务线程在外部死等;
- 第三道防线:多级降级逃生舱(Fallback Strategy):
- 一级降级:秒级热切换到备用模型供应商(例如从公有云模型降级到内网私有部署的小模型);
- 二级降级:从 Redis 语义缓存中提取近似的历史保底问答;
- 三级降级:输出业务兜底话术(如“当前咨询人数较多,正在为您转接人工客服”),死保接口 200 返回。
基于 Resilience4j 与 Spring AI 的生产配置
在 Spring Boot 3 中,我们通过引入resilience4j-spring-boot3模块,为ChatModel提供无缝的切面保护:
resilience4j: retry: instances: aiChatRetry: max-attempts: 3 wait-duration: 500ms enable-exponential-backoff: true exponential-backoff-multiplier: 2 retry-exceptions: - java.net.SocketTimeoutException - org.springframework.web.client.ResourceAccessException ignore-exceptions: - org.springframework.ai.retry.NonTransientAiException # 业务参数错误或敏感词拦截绝不重试 circuitbreaker: instances: aiChatBreaker: sliding-window-type: COUNT_BASED sliding-window-size: 20 minimum-number-of-calls: 10 failure-rate-threshold: 50 slow-call-rate-threshold: 70 slow-call-duration-threshold: 8000ms wait-duration-in-open-state: 15000ms # 熔断 15 秒后进入半开状态试探 permitted-number-of-calls-in-half-open-state: 3编程式容灾调用与双模型无缝切换
在业务 Service 层面,我们通过装饰器模式把主模型与备用模型组合起来,实现端到端的安全逃逸:
@Service public class ResilientAiService { private static final Logger log = LoggerFactory.getLogger(ResilientAiService.class); private final ChatModel primaryChatModel; // 主模型(如商业旗舰模型) private final ChatModel secondaryChatModel; // 备用模型(如内网私有化部署模型) private final CircuitBreaker circuitBreaker; private final Retry retry; public ResilientAiService( @Qualifier("primaryModel") ChatModel primaryChatModel, @Qualifier("secondaryModel") ChatModel secondaryChatModel, CircuitBreakerRegistry circuitBreakerRegistry, RetryRegistry retryRegistry) { this.primaryChatModel = primaryChatModel; this.secondaryChatModel = secondaryChatModel; this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("aiChatBreaker"); this.retry = retryRegistry.retry("aiChatRetry"); } public String safeCall(String userPrompt) { // 装饰主模型调用:Retry -> CircuitBreaker -> Primary Call Supplier<String> decoratedCall = CircuitBreaker.decorateSupplier(circuitBreaker, Retry.decorateSupplier(retry, () -> { Prompt prompt = new Prompt(userPrompt); return primaryChatModel.call(prompt).getResult().getOutput().getText(); }) ); // 使用 Try 执行并挂载 Fallback return Try.ofSupplier(decoratedCall) .recover(CallNotPermittedException.class, e -> { // 断路器开启状态:主模型已熔断,无感秒切备用模型 log.warn("[主模型已熔断] 自动无感降级至备用本地模型承接"); return callFallbackModel(userPrompt); }) .recover(Throwable.class, e -> { // 重试耗尽或其他未预料异常 log.error("[大模型全链路调用异常] 触发终极业务兜底: {}", e.getMessage()); return "当前咨询高峰,排队人数较多,请稍后刷新重试或联系人工客服。"; }) .get(); } private String callFallbackModel(String userPrompt) { try { Prompt prompt = new Prompt(userPrompt); return secondaryChatModel.call(prompt).getResult().getOutput().getText(); } catch (Exception ex) { log.error("备用模型亦发生故障,直接出保底兜底话术", ex); return "服务正在紧急维护中,请稍后再试。"; } } }生产落地避坑红线
- 坚决不要在 400/401 错误上重试:如果是因为 Prompt 包含了非法字符被模型网关拒绝(400 Bad Request),或者是 API Key 过期(401 Unauthorized),重试 100 次也是徒劳,反而会进一步放大延迟;
- 熔断半开(HALF_OPEN)探测的流量隔离:当断路器尝试从 OPEN 恢复到 CLOSED 时,只能允许极少数(如 3 笔)真实请求去探路。若探路请求依然超时,立即再次打回 OPEN,绝对不能在此时把洪峰流量瞬间放进来;
- 降级结果必须带有明确标识:在给前端返回的数据结构中,建议带上
fallback: true标记。前端可据此在 UI 界面给用户做出温和提示(例如展示一个小黄条:“当前回答由轻量离线模型生成”),在保障体验透明度的同时规避法律与服务纠纷; - 流式调用(Streaming)熔断器的特殊处理:很多团队发现普通方法的断路器包装在
Flux<String>流式调用上失效了。这是因为流式接口在返回Flux对象时方法调用其实就已经结束了,后续的数据帧是在后台 Reactive 线程中持续推送的。保护流式接口必须使用 Project Reactor 的原生操作符,如Flux.onErrorResume()与 Resilience4j Reactor 模块提供的CircuitBreakerOperator.of(circuitBreaker),才能精准捕获到流式传输中途发生的网络中断或超时; - 基于健康度评分的自适应流量切分:不要等到主模型彻底不可用(错误率超 50%)才一刀切地全部转去备用模型。更高级的生产方案是建立实时的“健康评分中枢”,根据各供应商过去 1 分钟内的平均 TTFT 延迟与成功率,动态按比例分流(如 80% 给主模型,20% 给备用模型;主模型延迟恶化时动态调整为 50%/50%),以平滑的方式消化突发抖动。