05 调大模型API和调支付宝接口一模一样
摘要本文系统阐述了大模型API调用中的容错与高可用设计。核心思路是将大模型API视为不可靠的外部依赖通过四层防护机制保障系统稳定性1线程池隔离避免慢响应拖垮业务线程2智能重试针对超时和限流进行指数退避重试3熔断降级当失败率过高时自动切断调用链路4多Provider切换在主模型不可用时自动降级到备用模型。文章结合Spring生态的Retryable、Resilience4j CircuitBreaker等工具给出了完整的Java代码实现并提供了面试场景的标准回答模板。这篇聊一个面试必问、但大部分人准备不足的话题。你调大模型 API 的时候考虑过它挂了怎么办吗很多人没想过。Demo 阶段嘛调一次成功一次没出过问题。但我问你个场景你的知识库上线了用了 GPT-4o。某天下午三点OpenAI 的某个节点出问题了——不是你一个人的问题是全球性的。你的用户正在往系统里问问题突然全部返回超时错误。你怎么办有人说我代码里没处理超时默认 30 秒连接超时。用户等 30 秒拿到一个白屏刷新一下再等 30 秒。然后老板的微信来了你的系统坏了。有人说我用 try-catch 包了一下超时了返了个服务繁忙。比上一位好点但用户在你这拿不到答案转头就去问别的同事了你的系统慢慢被弃用。这两种情况我都见过。而且说句实话第二种已经算不错了——至少没让用户看到异常堆栈。我打个比喻你们感受一下调大模型 API跟你在美团点外卖一模一样。你下单了等着骑手把饭送来。但这个骑手可能路上摔跤了请求超时、可能拿错了餐返回乱码、可能点了个已取消的商家API 挂了、可能堵路上了响应特别慢。你是点餐的人你能怎么办你等一会儿再点一次——重试。你换一家店点——降级到另一个模型。你设置最长等待时间——超时。你觉得今天这家店不行直接不吃了——熔断。一模一样。没有任何本质区别。你平时调支付宝、调微信支付、调短信通道怎么做的兜底策略调大模型 API 就怎么做。先说核心线程池独立这是 90% 的人踩的第一个坑。大模型 API 的响应时间是不可控的。好的时候 1 秒慢的时候 10 秒挂了的时候 30秒超时才回错误。如果不做隔离你的业务线程会被大模型的慢响应拖死。举个真实例子你的系统是个 Web 服务Tomcat 默认 200 个线程。突然来了一波用户每人问一个很长的 Prompt。大模型卡住了200 个线程全卡在等待 API 返回上。这时候新请求来了线程池满了Tomcat 拒绝连接。你的整个系统挂了——不是因为你的业务逻辑有问题是因为调大模型 API 把线程池占完了。这不是假设。我亲耳听过一个案例他们用默认的 RestTemplate 调 OpenAI生产上 400 并发直接全部 503。解决方案大模型的 API 调用必须走独立的线程池。Configuration public class LlmThreadPoolConfig { Bean(llmTaskExecutor) public ThreadPoolTaskExecutor llmTaskExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); // 核心线程大模型慢并发不宜太高 executor.setCorePoolSize(10); // 最大线程最多允许 20 个并发请求同时等大模型 executor.setMaxPoolSize(20); // 队列容量最多排队 100 个请求 executor.setQueueCapacity(100); // 线程名前缀方便排查 executor.setThreadNamePrefix(llm-worker-); // 任务拒绝策略直接抛异常让调用方感知到限流了 executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); executor.initialize(); return executor; } }注意几个关键点corePoolSize 10—— 核心线程数。为什么设这么小因为大模型 API 的瓶颈通常不在你的机器上在远端 API 的并发限制上。很多大模型 API 对单个 IP 有 QPS 限制你起 100 个线程去请求95 个被限流返回 429。queueCapacity 100—— 队列容量。超过 100 个就在入口处拒绝不要让你的系统死在大模型手上。CallerRunsPolicy—— 当线程池满了让调用者线程自己跑。这个策略很聪明Web 请求的线程被卡在大模型调用上相当于它自己替大模型扛了一部分并发。比AbortPolicy直接抛异常更温和。Retryable 重试——别一次失败就放弃有时候大模型 API 挂了不是真挂了是短暂波动。过几秒又好了。比如网络抖了一下、API Gateway 重启、你 API Key 的配额刚好到期需要刷新。这些情况重试一次就能搞定。Spring 的Retryable注解一行就搞定了Service public class LlmRetryService { Retryable( retryFor {TimeoutException.class, HttpClientErrorException.TooManyRequests.class}, maxAttempts 3, backoff Backoff(delay 1000, multiplier 2) ) public String callLlm(String prompt) { ResponseEntityString response restTemplate.postForEntity( openAiUrl, buildRequest(prompt), String.class ); return response.getBody(); } Recover public String recover(Throwable e, String prompt) { // 三次重试都失败后做兜底 return 系统暂时繁忙请稍后再试; } }retryFor—— 什么异常触发重试超时和限流429值得重试。4xx 其他错误比如 401 认证失败不值得重试一百次也没用。backoff Backoff(delay 1000, multiplier 2)—— 重试间隔。第一次等 1 秒第二次等 2 秒。为什么递增因为对端如果正在压力恢复中你疯狂重试反而会加重对端负载。指数退避是对双方都友好的策略。Recover—— 三次全失败后调这个方法。你在这里做降级返回一个友好的提示、或者走缓存、或者调一个更便宜的备用模型。Resilience4j 熔断——别让错误连锁反应重试对短时波动有效。但如果大模型真的挂了比如 API 提供商宕机了重试只会让你的线程池雪上加霜。这时候需要熔断。一句话解释你连续失败了很多次之后就别再试了直接走降级。Resilience4j 的 CircuitBreaker 是 Spring Cloud 生态里的标配Bean public CircuitBreaker llmCircuitBreaker() { CircuitBreakerConfig config CircuitBreakerConfig.custom() // 10 秒窗口内超过 50% 的请求失败就熔断 .failureRateThreshold(50) .slidingWindowSize(10) // 熔断后等待 30 秒再尝试恢复 .waitDurationInOpenState(Duration.ofSeconds(30)) // 半开状态允许 3 个请求试探 .permittedNumberOfCallsInHalfOpenState(3) .build(); return CircuitBreakerRegistry.of(config) .circuitBreaker(llm-api, config); }参数含义failureRateThreshold(50)—— 失败率阈值 50%。窗口内 10 个请求有 5 个失败就熔断。slidingWindowSize(10)—— 滑动窗口大小 10 个请求。注意是最后一个请求开始往前数 10 个不是固定的时间窗口。这样更能反映当前状态。waitDurationInOpenState(30)—— 熔断后等 30 秒才能进入半开状态。给对端足够时间恢复。permittedNumberOfCallsInHalfOpenState(3)—— 半开状态只放 3 个请求去试探。如果这 3 个都成功了电路关闭恢复正常。如果还有失败的继续熔断。使用方式Service public class LlmApiService { Autowired private CircuitBreaker llmCircuitBreaker; private final RestTemplate restTemplate; public String callWithCircuitBreaker(String prompt) { return llmCircuitBreaker.executeSupplier(() - { // 熔断状态下这一行不会被执行 // 会直接抛出 CircuitBreakerOpenException return restTemplate.postForEntity( openAiUrl, buildRequest(prompt), String.class ).getBody(); }); } }当熔断器打开时executeSupplier不会真的发起 HTTP 请求而是直接抛出异常。你可以在这个异常的地方捕获走降级。多 provider 切换——别在一棵树上吊死靠模型提供商活着的系统最怕的是它的 API 挂了。但你只有一个 API Key。正确答案是多备几个 provider自动切换。Component public class LlmRouter { private final ListLlmProvider providers; private final CircuitBreaker circuitBreaker; public String call(String prompt) { for (LlmProvider provider : providers) { if (!provider.isAvailable()) { log.warn({} 不可用切换到下一个, provider.name()); continue; } try { return circuitBreaker.executeSupplier( () - provider.call(prompt) ); } catch (Exception e) { log.warn({} 调用失败切换到下一个, provider.name(), e); } } // 所有 provider 都挂了最后的降级 return fallback(prompt); } private String fallback(String prompt) { // 你可以选择走本地小模型或者返回缓存 return localModel.call(prompt); } }LlmProvider接口抽象了模型调用public interface LlmProvider { String name(); boolean isAvailable(); String call(String prompt); }你可以给 OpenAI 一个实现、给通义千问一个实现、再给本地部署的 Ollama 一个实现。排好优先级失败了自动按顺序往下走。fallback方法调用localModel—— 这可以是你本地部署的一个小模型比如 Qwen2.5 7B。性能不如 GPT但至少不会让用户空手而归。综合完整的调用链路把上面的东西串在一起实际的调用链路是这样的Service public class LlmResilientService { Autowired Qualifier(llmTaskExecutor) private ThreadPoolTaskExecutor executor; Autowired private LlmRouter router; public CompletableFutureString ask(String prompt) { // 异步提交到独立线程池不阻塞业务线程 return CompletableFuture.supplyAsync(() - { try { return router.call(prompt); } catch (Exception e) { log.error(所有模型调用都失败了, e); return 系统繁忙请稍后重试; } }, executor) // 给整个调用设置超时最多等 15 秒 .orTimeout(15, TimeUnit.SECONDS) .exceptionally(ex - { log.warn(LLM 调用超时, ex); return 回答超时请简化后重试; }); } }这个类做的事情1. 把请求丢到独立的 LLM 线程池不占 Tomcat 的线程2. 用 LlmRouter 自动切换多个 providerA 不行换 B3. 每个 provider 调用都有 Resilience4j 熔断器保护4. 设置了全局超时 15 秒你给前端返回CompletableFuture前端可以展示 loading 图标等结果回来再刷新。不会让用户干等。 面试官视角的标准回答如果面试官问大模型 API 挂了你怎么保证系统可用性我把它当作一个外部依赖来处理和调支付宝没区别。从四个层面做brbr第一是线程隔离。大模型 API 的响应时间不可控必须走独立的线程池。我设了 10 个核心线程、100 个排队上限超过的直接拒绝不占 Tomcat 的 worker 线程。brbr第二是超时和重试。必须显式设置连接超时和读取超时默认的 infinite 是生产大忌。超时后用 Retryable 做指数退避重试最多 3 次。brbr第三是熔断降级。用 Resilience4j 的 CircuitBreaker50% 失败率触发熔断30 秒后自动恢复。熔断期间走降级逻辑比如返回缓存数据或友好提示。brbr第四是多 provider 切换。主备模型策略OpenAI 挂了自动切到通义千问再不行切到本地 Ollama。保证不管怎么出问题用户都不会拿到白屏。brbr核心就一句话任何外部依赖都不可靠你要做的不是防它不挂是它挂了你的系统还能跑。下一篇是这个系列的最后一篇RAG 调优。chunk 到底切多大top_k 设多少怎么评估你的 RAG 效果好不好私信回复「666」一次性领走面试宝典Java 高频考点速查表、HashMap/ConcurrentHashMap 源码笔记、JVM 调优案例、Spring Boot 面试 50 问AI 编程工具箱Cursor/Copilot/Codex 六工具对比表、10 个 Prompt 模板、Debug 万能公式、Cursor 速查手册、AI 图片生成入门、30 效率工具包一份资料包两个专栏都能用。「唠点键盘之外的」只讲干货。

相关新闻

springboot项目父子工程整项目打包太大问题解决

springboot项目父子工程整项目打包太大问题解决

springboot项目部署虽然简单&#xff0c;但是将一整个项目打成一个包的话就会非常大&#xff0c;每次对项目进行微调的时候就会非常痛苦&#xff0c;所以接下来就是处理这个问题的办法&#xff1a;<build><plugins><plugin><groupId>org.apache.maven.…

2026/10/25 22:32:29 阅读更多 →
Linux进阶篇05:systemd 深度实战

Linux进阶篇05:systemd 深度实战

环境声明&#xff1a;VirtualBox 虚拟机&#xff0c;Rocky Linux 9&#xff0c;Legacy BIOS (非 UEFI)&#xff0c;最小化安装/Server 环境。 前置知识&#xff1a;已完成《Rocky 9 启动流程》《GRUB2 深度配置》学习&#xff0c;理解 systemd 是内核启动后第一个用户态进程&am…

2026/10/24 17:16:41 阅读更多 →
微信支付查看证书序列号下载平台证书商家付款配置

微信支付查看证书序列号下载平台证书商家付款配置

要2个证书号serial直接进微信商户号查看&#xff0c;第二个pt_serial需要命令行生成1.查看证书序列号,进入到apiclient_cert.pem所在目录openssl x509 -in apiclient_cert.pem -noout -serial微信支付v2 v3密钥必须32位&#xff0c;不然可保存&#xff0c;但使用会报签名错误2…

2026/10/25 2:26:22 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 12:00:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →