上游LLM厂商又抽风了-我们的多渠道故障转移是怎么做的
上游 LLM 厂商又抽风了我们的多渠道故障转移是怎么做的摘要本文详细介绍了AI平台中多渠道故障转移系统的设计与实现。通过拆分渠道与厂商表结构、建立三态健康状态机、精准判断故障切换条件网络异常/5xx切4xx/429不切、实现failover主循环与后台探活机制、优化缓存策略及错误信息脱敏构建了一个稳定可靠的故障转移系统。文章还分享了实际开发中遇到的坑点与解决方案为构建高可用LLM网关提供了实践参考。接入过多个 LLM 厂商的人都知道一个痛苦的事实——它们时不时会挂。移动云抽个风、智谱限个流、某个 Key 突然失效这些都是家常便饭。如果只接一个渠道用户体验就是时好时坏随时可能 502。我们这个 AI 平台接了好几个厂商同一个模型往往配了多个上游渠道比如 GLM-5.2 既可以从移动云走也可以直连智谱。需求很明确主渠道挂了网关得自动切到备用渠道用户无感知。这篇讲讲这个故障转移系统怎么做的重点聊聊什么错误该切、什么错误不该切这个容易踩坑的判断。先把数据模型理清楚做故障转移第一步得知道有哪些渠道可以切。我们用了两张表渠道和厂商是拆开的。一开始没拆渠道表里直接存upstream_url和upstream_api_key。结果发现一个问题同一个厂商账号同一套 URLKey会被多个模型引用。比如移动云的一个账号既调 GLM 又调 Qwen 又调 DeepSeek那这套 URLKey 就得在三个渠道记录里各填一遍。这带来的麻烦是——Key 要轮换的时候得到每个渠道记录里去改十几个地方漏一个就是隐患。而且重复数据本身就难维护。所以后来拆了-- 厂商表一个厂商账号一份配置CREATETABLEt_provider(idBIGINTPRIMARYKEY,nameVARCHAR(100),-- 移动云-生产1号upstream_urlVARCHAR(500),-- 上游 URLupstream_api_keyVARCHAR(500),-- Keytimeout_secINTDEFAULT60);-- 渠道表模型 × 厂商的关联一个模型可挂多个厂商CREATETABLEt_model_channel(model_idVARCHAR(100),-- 对外模型 IDglm-5.2provider_idBIGINT,-- 关联厂商账号upstream_modelVARCHAR(100),-- 上游要求的模型名ZHIPU/GLM-5.2priorityINTDEFAULT0,-- 0主1/2备用weightINTDEFAULT100,health_statusVARCHAR(20)-- HEALTHY/DEGRADED/DOWN);拆完清爽了渠道只管模型和厂商怎么关联、用什么路由策略连接配置全归厂商表。Key 轮换改一处所有引用的渠道自动生效。这个拆分虽然只是个表结构调整但省了无数维护麻烦。健康状态机把坏渠道踢出轮询光有多渠道不够。如果某个渠道已经挂了每次请求还去试一遍它那就是白白浪费时间和用户体验。得有个机制把确认坏掉的渠道临时踢出去。每个渠道有个health_status字段三态连续失败 ≥ 3 次 HEALTHY ─────────────────→ DEGRADED降级仍参与路由但标记 ↑ │ │ │ 连续失败 ≥ 5 次 │ 探活成功 ▼ └──────────────────────── DOWN宕机从路由剔除 │ │ 后台探活成功 ▼ 恢复 HEALTHY记录调用结果的逻辑defrecord_channel_call(channel_id,success,error_msgNone):withget_conn()asconn:withconn.cursor()ascursor:ifsuccess:# 成功清零连续失败恢复健康cursor.execute(UPDATE t_model_channel SET total_calls total_calls 1, consecutive_failures 0, health_status HEALTHY WHERE id %s,(channel_id,))else:# 失败连续失败 1到阈值自动降级/下线cursor.execute(UPDATE t_model_channel SET total_calls total_calls 1, consecutive_failures consecutive_failures 1, health_status CASE WHEN consecutive_failures 1 5 THEN DOWN WHEN consecutive_failures 1 3 THEN DEGRADED ELSE health_status END WHERE id %s,(channel_id,))阈值为什么是 3 和 5这是经验值。3 次降级是给运维一个早期信号——“这渠道开始不稳定了”5 次下线是因为连续失败 5 次基本可以确定是真挂了不是偶发抖动。如果只失败 1 次就下线网络抖动一下就把渠道踢了太激进。查询可用渠道时DOWN 的直接过滤defget_channels(model_id):withget_conn()ascursor:cursor.execute(SELECT ... FROM t_model_channel mc LEFT JOIN t_provider p ON p.id mc.provider_id WHERE mc.model_id %s AND mc.status ACTIVE AND mc.health_status IN (HEALTHY, DEGRADED) # DOWN 不参与ORDER BY mc.priority ASC, mc.weight DESC,(model_id,))最关键的判断什么错误该切什么不该切这是整个设计里想得最久的地方。不是所有错误都应该切换渠道。先看代码里的判断函数很短但很关键def_should_failover(status_code,exc):判断是否该切到备用渠道。ifexcisnotNone:returnTrue# 网络异常/超时/连接拒绝 → 切ifstatus_codeisnotNoneandstatus_code500:returnTrue# 上游 5xx → 切returnFalse# 4xx 和 429 都不切逻辑看着简单但背后的考量挺多。网络异常/超时exc→ 切。这是最明确的该切的情况——主渠道连不上、响应超时换一个试。5xx → 切。上游服务器内部错误换个渠道可能就好。4xx → 不切。这个要展开说。4xx 是客户端错误意味着你的请求有问题。比如400 参数错误换一个渠道参数还是错的照样 400。401/403 鉴权失败是这个渠道的 Key 有问题。理论上该切——但实际场景里如果这个 Key 失效悄悄切到备用反而掩盖了问题备用可能也用同厂商同账号。我们选择报错让人看到让运维去修 Key而不是静默切走。404 模型不存在上游路由配错了切了也一样。所以 4xx 的处理是不切把错误返回给上层让用户/运维看到真实问题。429限流→ 不切。这个最容易引起争论。429 是这个账号触发了厂商限流理论上切到另一个账号能缓解。但我们选择不切原因有二限流通常是账号维度的切到同厂商的备用渠道一样限流。如果是真·突发流量切走只是把限流传染到备用渠道没解决问题。429 应该靠前端退避重试解决而不是网关悄悄切渠道。当然如果你的场景是不同厂商账号互为备份429 也可以做成可切换的——我们把它做成策略可配但默认不切。这个该不该切的判断是整个故障转移设计的灵魂。切得激进会掩盖真实问题切得保守用户体验差。得根据业务场景权衡。failover 主循环判断逻辑定了主循环就好写了——按优先级逐个渠道试失败就切asyncdefcall_with_failover(model,body,streamFalse):channelsget_channels(model)# 已按优先级排序DOWN 已过滤ifnotchannels:returnNone,None# 没有多渠道配置回退旧路由clientawait_get_client()last_errorNoneforidx,chinenumerate(channels):req_bodybody.copy()ifch.get(upstream_model)!model:req_body[model]ch[upstream_model]# 映射上游模型名headers{Authorization:fBearer{ch[upstream_api_key]},Content-Type:application/json,}role主渠道ifidx0elsef备用渠道{idx}logger.info(fFailover [{role}]{model}-{ch[upstream_url][:50]})try:reqclient.build_request(POST,ch[upstream_url],jsonreq_body,headersheaders,timeoutch.get(timeout_sec,60))responseawaitclient.send(req,streamstream)# 2xx 或 4xx不切换4xx 换渠道也一样ifresponse.status_code500:returnresponse,ch# 返回给上层处理# 5xx记录失败切下一个logger.warning(fFailover [{role}] 返回{response.status_code}切换)_record_fail(ch,fHTTP{response.status_code})awaitresponse.aclose()# ← 别忘了关否则 FD 泄漏last_errorException(fHTTP{response.status_code})exceptExceptionase:# 网络异常记录失败切下一个logger.warning(fFailover [{role}] 异常{e}切换)_record_fail(ch,str(e)[:500])last_errore logger.error(fFailover 所有渠道挂了{model}, last_error{last_error})returnNone,None这里有个血泪教训——5xx 响应的response.aclose()不能漏。流式响应拿到后无论成功失败都要关。之前有版本切换渠道时忘了关 5xx 的响应导致连接不回收、FD 泄漏就是那篇 502 排查的锅之一。现在每次切换前都老老实实 close。失败记录是异步的不阻塞请求def_record_fail(ch,error_msg):cidch.get(channel_id)ifcid:asyncio.create_task(asyncio.to_thread(record_channel_call,cid,False,error_msg))用create_task丢到后台线程写库不拖慢用户的这次请求。渠道挂了不能一直不管后台探活渠道被标记 DOWN 后不能永久拉黑——上游可能只是临时抽风过会儿就好了。所以得有个后台循环定期去戳一下DOWN 的渠道活了就恢复。asyncdefprobe_down_channels():# 查所有 DOWN 渠道rowsquery(SELECT ... WHERE health_status DOWN)clientawait_get_client()forchinrows:# 发最小探针max_tokens1几乎不花钱test_body{model:ch[upstream_model],messages:[{role:user,content:ping}],max_tokens:1,}try:respawaitclient.send(client.build_request(POST,ch[upstream_url],jsontest_body,...))# 任何非 5xx 都算上游可达恢复健康ifresp.status_code500:restore_health(ch[channel_id])logger.info(f渠道{ch[channel_id]}探活成功恢复 HEALTHY)exceptException:pass# 探活失败就保持 DOWN下轮再试asyncdefstart_probe_loop(interval_sec180):whileTrue:awaitprobe_down_channels()awaitasyncio.sleep(interval_sec)# 每 3 分钟一轮探针设计有几个讲究max_tokens: 1让上游只生成 1 个 token 就停几乎不花钱。如果发完整请求探活成本扛不住。判定标准是 500而不是 200。这个改过——最早判 200但有些厂商对余额不足返回 200 body 里带错误码探活会误判健康。改成 500只要上游系统可达就算活把业务错误排除在健康判定外。401 可能只是 Key 临时问题429 是限流这些是软故障不该让渠道长期下线。探活恢复后主动刷缓存让路由查询立刻看到ifrestored0:refresh_cache()# 清空渠道缓存强制下个请求重查缓存别每次请求都查库故障转移每次都要查t_model_channel JOIN t_provider这是个 JOIN 查询每个对话请求都查一遍太浪费。做了两级缓存_single_cache{}# 单渠道TTL 5 分钟_list_cache{}# 渠道列表TTL 30 秒defget_channel(model_id):cached_single_cache.get(model_id)ifcachedandtime.time()-cached[0]300:# 5 分钟returncached[1]# 未命中查 DB写缓存...defget_channels(model_id):cached_list_cache.get(model_id)ifcachedandtime.time()-cached[0]30:# 30 秒returncached[1]...TTL 的取舍单渠道配置很少变5 分钟无所谓渠道列表涉及健康状态渠道下线/恢复要快一点生效30 秒是个平衡。探活恢复时主动refresh_cache()让刚恢复的渠道立刻被看到不等 30 秒。错误信息脱敏顺带提一个细节。上游 LLM 厂商的错误消息经常带商业信息比如请到 ecloud.10086.cn 订购资源包。这种东西直接透传给用户既不专业也可能暴露商业关系。所以做了个错误脱敏SENSITIVE_WORDS[订购,ecloud.10086.cn,订阅,资源包,签约]defsanitize_error_response(content,status_code):# 脱敏商业信息 → 请求失败# 按错误码给友好中文429→请求频繁、401→认证失败、502→服务异常...用户看到的是统一的中文友好提示而不是上游的英文技术串和订购引导。这种小细节对用户体验的提升比想象中大。踩过的坑坑一5xx 响应不关闭 → FD 泄漏。前面提过了failover 切换时response.aclose()不能漏否则就是那篇 502 的事故。坑二探活把余额不足误判为健康。最早判status_code 200但厂商对余额不足也返 200导致探活把实际不可用的渠道恢复回去。改成 500解决。坑三并发写健康状态丢更新。record_channel_call用consecutive_failures consecutive_failures 1这种读改写高并发下可能两个请求同时失败但只 1。后来改成短连接 单条 UPDATE 原子操作。坑四缓存没及时刷新。管理后台改了渠道 Key但缓存 5 分钟没过期请求还在用老 Key 失败。后来管理后台改配置后主动调refresh_cache()。最后多渠道故障转移看着是个路由 重试的简单事做细了涉及的点不少表设计拆分、健康状态跟踪、切换判断精准、探活自愈、缓存平衡、错误脱敏。核心的设计思想就一条不乱切。网络异常/5xx 果断切4xx/429 不切——因为换了也一样甚至更糟。配合前面那篇计费协议整个网关做到了故障自动转移、扣费准确、用户透明。上游抽风的时候用户基本无感地切到了备用渠道这就是这套系统的价值。

相关新闻

九大网盘直链解析工具:告别限速下载的完整解决方案

九大网盘直链解析工具:告别限速下载的完整解决方案

九大网盘直链解析工具:告别限速下载的完整解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘…

2026/8/12 6:00:36 阅读更多 →
学了那么多,为什么业绩就是不见涨?

学了那么多,为什么业绩就是不见涨?

这大概是很多老板心里最扎心的问题。课没少听,笔记没少记,各种管理理论张口就来。可一年到头算算账,销售额还是那个数字,利润还是那个水平。你开始怀疑:是不是学习本身没用?是不是我的行业不行了&#xff1…

2026/8/12 5:59:39 阅读更多 →
如何5分钟掌握Android投屏:Escrcpy图形化控制完整教程

如何5分钟掌握Android投屏:Escrcpy图形化控制完整教程

如何5分钟掌握Android投屏:Escrcpy图形化控制完整教程 【免费下载链接】escrcpy 📱 Display and control your Android device graphically with scrcpy. 项目地址: https://gitcode.com/GitHub_Trending/es/escrcpy 你是否还在为复杂的ADB命令而…

2026/8/12 6:00:42 阅读更多 →

最新新闻

Windows原地升级助手:轻松实现系统版本自由切换

Windows原地升级助手:轻松实现系统版本自由切换

Windows原地升级助手:轻松实现系统版本自由切换 【免费下载链接】In-Place_Upgrade_Helper Helper-Tool for Windows 10/11/Server In-Place-upgrades and changing between Windows Editions 项目地址: https://gitcode.com/GitHub_Trending/in/In-Place_Upgrade…

2026/8/11 23:15:36 阅读更多 →
High Speed Scanner

High Speed Scanner

High Speed Scanner 高速扫描仪

2026/8/11 23:15:36 阅读更多 →
昇腾AI智能体自动管理安卓应用

昇腾AI智能体自动管理安卓应用

针对昇腾Model-Agent模型适配大赛第二季中,基于昇腾(Ascend)与AtomGit AI技术实现安卓手机应用的自动化删除与安装任务,其核心在于构建一个具备环境感知、决策规划和自动化执行能力的智能体(Agent)。该Agen…

2026/8/11 23:15:36 阅读更多 →
能源行业业扩报装自动化方案:基于大模型Agent的电力营销数智化转型实践

能源行业业扩报装自动化方案:基于大模型Agent的电力营销数智化转型实践

在能源行业数字化转型与新型电力系统建设的宏观背景下,业扩报装作为电力营销服务的核心环节,其自动化方案正经历从传统人工流程向数智化驱动的深刻变革。随着《新型电力系统建设“十五五”规划》的发布,电网企业提升营销数字化水平已成为必然…

2026/8/11 23:15:36 阅读更多 →
如何在5分钟内搭建免费Web POS系统:NexoPOS完整实战指南

如何在5分钟内搭建免费Web POS系统:NexoPOS完整实战指南

如何在5分钟内搭建免费Web POS系统:NexoPOS完整实战指南 【免费下载链接】NexoPOS Laravel-based web POS system with Vue.js, Tailwind CSS, inventory management, sales processing, customer records, reports, and modular extension support. 项目地址: ht…

2026/8/11 23:15:35 阅读更多 →
2025年Web开发者三阶段成长指南:从基础技能到全栈架构的完整路线图

2025年Web开发者三阶段成长指南:从基础技能到全栈架构的完整路线图

2025年Web开发者三阶段成长指南:从基础技能到全栈架构的完整路线图 【免费下载链接】developer-roadmap-chinese 2021 年成為 Web 開發人員的路線圖 台灣正體中文版 项目地址: https://gitcode.com/gh_mirrors/de/developer-roadmap-chinese 在技术快速迭代的…

2026/8/11 23:14:35 阅读更多 →

日新闻

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/12 1:11:09 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/12 1:11:09 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/12 1:11:08 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/11 17:09:45 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/12 1:11:10 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/11 17:09:45 阅读更多 →