AI 网关多上游负载均衡:原理、策略与配置实践
当你在 AI 网关中配置了同一模型的多个上游渠道(如同时有两个 OpenAI 账号、或一个 OpenAI 加一个 Azure OpenAI),负载均衡决定每个请求发给哪个上游。合理的负载均衡既能提升吞吐量,又能降低单点故障风险。
我见过不少团队是被一次凌晨的 429 洪水才逼着去补这一课的:白天流量正常,晚上跑批处理任务时并发一下冲到平时的三四倍,单账号的 RPM 打满,前端疯狂重试又进一步加剧限速,最后整条链路卡死。事后复盘发现,其实账号里还有另一个几乎没怎么用的备用 key,只是网关配置里从来没把它纳入负载均衡池——流量全砸在一个渠道上,第二个渠道形同摆设。这类问题不是靠加机器解决的,是配置层面的疏漏,而且往往要等出事才会被发现。
为什么需要多上游负载均衡
单上游的典型瓶颈:
| 问题 | 表现 |
|---|---|
| RPM/TPM 限速 | 单账号每分钟请求数有上限,并发高时频繁 429 |
| 单点故障 | 该账号被封禁、欠费、服务商故障,业务全停 |
| 地域延迟 | 单一 API 端点可能在某些时段延迟高 |
| 成本均摊 | 多账号分流可利用各账号的免费额度或优惠价 |
配置多上游后,网关在这些渠道间分发流量,实现水平扩容和冗余。
主流负载均衡策略
轮询(Round Robin)
最简单的策略:请求按顺序依次分配给各上游。
请求 1 → 上游 A
请求 2 → 上游 B
请求 3 → 上游 C
请求 4 → 上游 A(循环)
优点:实现简单,流量均匀分配。
缺点:不考虑各上游的处理能力差异,也不感知当前负载。
这个缺点在实际生产里体现得很直接:假设上游 A 是官方 OpenAI(RPM 上限 3500),上游 B 是某个二级代理商中转的渠道(RPM 上限只有 500),纯轮询会给两边分同样多的请求。结果 A 还有大把余量,B 已经先被打到限速——你看到的现象是”明明配了两个上游还是频繁 429”,根因其实是轮询完全不看各自的承载能力。这也是为什么生产环境里几乎不会单独用轮询,它更适合上游能力完全对等(比如同一账号下开的两个 API Key)的场景,作为分流的基础手段。
加权轮询(Weighted Round Robin)
为每个上游配置权重,权重越高分配的流量越多。
# OneAPI 渠道配置示例
channels:
- name: "OpenAI 主账号"
weight: 3 # 分配 60% 流量
- name: "Azure OpenAI"
weight: 2 # 分配 40% 流量
适用场景:上游性能或配额不同时,按比例分配;测试新渠道时用低权重灰度。
这里有个容易被忽略的实现细节:加权轮询不等于”按比例简单排队”。如果按最朴素的方式实现——权重 3:2 就排成 A A A B B,你会发现请求在时间轴上是成串扎堆的,前三个请求全打给 A,A 的瞬时并发压力比预期高得多,B 又会有一段时间完全闲置。生产级实现(Nginx、LVS 用的都是这个思路)会用平滑加权轮询算法打散顺序,让同样是 3:2 的权重呈现成 A B A A B 这样交替的序列,瞬时压力更均匀。如果你自己写网关的负载均衡模块,这个坑一定要避开——权重配置对了,分配顺序错了,效果照样打折扣。判断你用的网关是不是”朴素轮询”很简单:连续观察 10~20 个请求的上游分布,如果总是成串出现同一个上游,就该留意瞬时并发是否会顶到上游的并发上限。
最少连接数(Least Connections)
将新请求分配给当前活跃连接数最少的上游,适合请求处理时间差异大的场景(流式响应时间长短不一)。
它解决的是轮询和加权轮询都解决不了的问题:轮询只看”分了几个请求”,不看”这几个请求现在还占着多久”。举个例子,同样是 GPT-4 类模型,一个请求生成 50 个 token 可能半秒返回,另一个请求让模型写一篇长文可能占用连接 30 秒以上。如果恰好把几个”长任务”都轮询分给了同一个上游,即使请求数摊平了,这个上游的实际负载也会明显偏高。最少连接数会动态感知”谁现在压力小”,把新请求导过去,比静态的轮询/加权轮询更贴近真实负载。
但它也有一个反直觉的坑:连接数不等于负载。如果某个上游正在处理的都是轻量小请求,而另一个上游正在处理少数几个但很重的长文本生成,单纯数”连接数”可能会误判——连接少的那个反而更忙。更精细的实现会用”加权最少连接”,即连接数除以该上游的权重(代表处理能力)再比较,或者干脆用响应时间的滑动平均值来加权。如果你发现最少连接策略在长短请求混跑的场景下效果不如预期,先去查一下网关统计的到底是”连接数”还是”负载加权后的连接数”,这两者在文档里经常语焉不详,得实测确认。
优先级(Priority)
设置主/备关系:主上游正常时所有流量走主上游;主上游异常时才切到备用上游。
优先级 1(主):OpenAI 官方 API
优先级 2(备):Azure OpenAI
优先级 3(兜底):力达云聚合 API
这是容灾负载均衡的基础模式,详细机制见容灾与自动降级 failover。
优先级模式有个参数经常被漏配:主上游恢复后要不要立刻切回去。如果主上游只是短暂抖动(比如网络波动 3 秒后自愈),流量已经切到备用上游,这时候立刻切回主上游,会造成”切来切去”的抖动,尤其是对有状态的流式连接,切换意味着已经在传输的响应被打断重来。生产上更稳妥的做法是加一个”恢复观察期”:主上游连续健康检查通过 N 次(比如连续 3 次、间隔 10 秒)之后,才把新请求逐步切回去,而不是探测到一次成功就立刻全量切回。这个观察期具体设多长,取决于你对”抖动 vs 真故障”的容忍度——线上故障率高、对延迟不敏感的场景可以设长一点(1~2 分钟),对实时性要求高的场景可以适当缩短。
限速感知与动态权重
原始轮询不感知上游的限速状态。更智能的实现会:
- 捕获 429 响应:收到
429 Too Many Requests时,暂时降低该上游权重或跳过该上游,等待限速窗口重置。 - 指数退避:短时间内多次 429 后,该上游的”冷却时间”按指数增长,避免持续触发限速。
- 令牌桶追踪:网关本地维护每个上游的 TPM/RPM 计数器,在请求发出前预判是否会限速,提前换路由。
网关接收请求
│
├─ 检查本地 TPM 计数器
│ ├─ 上游 A 剩余配额充足 → 路由到 A
│ └─ 上游 A 配额接近上限 → 路由到 B(避免 429)
│
└─ 记录本次请求 token 数,更新计数器
实际排查 429 的时候,先别急着调策略,把上游返回的错误体完整打出来看一眼。以 OpenAI 风格的错误为例,典型响应是:
{
"error": {
"message": "Rate limit reached for gpt-4 in organization ... on requests per min (RPM): Limit 3, Used 3, Requested 1.",
"type": "requests",
"code": "rate_limit_exceeded"
}
}
注意这里的 type 字段——requests 代表撞的是 RPM 限速,tokens 代表撞的是 TPM 限速,两者的处理方式不一样:RPM 超限意味着这个上游短时间内不能再发新请求,网关应该整体降权;TPM 超限则说明单次请求的 token 消耗太大,即便换一个请求过去也可能马上再撞限速,这种情况换上游未必有用,更该做的是检查是不是有异常长的输入没有做截断。另外响应头里如果带了 Retry-After,网关应该直接读这个值作为冷却时间,而不是自己瞎猜一个固定的退避时长——用官方告诉你的等待时间,比自己拍脑袋估的更准,也能避免刚冷却完又立刻撞线的尴尬。
健康检查与上游剔除
负载均衡必须配合健康检查,及时将故障上游从候选列表中剔除:
| 健康检查类型 | 实现方式 | 说明 |
|---|---|---|
| 被动检查 | 统计错误率(5xx 比例) | 无额外开销,但发现故障有延迟 |
| 主动探活 | 定时发送轻量请求 | 实时感知,消耗少量 token 配额 |
| 熔断器 | 连续失败 N 次后断路 | 快速隔离故障上游,避免雪崩 |
熔断状态下,网关不再向该上游发送请求,定期尝试恢复(半开状态),成功后重新纳入负载均衡池。
一个可以直接照抄的经验阈值:30 秒内连续 5 次 5xx(或超时)触发熔断,熔断 60 秒后进入半开状态,放一个探测请求过去,成功则恢复、失败则再熔断 60 秒。这组数字不是拍脑袋定的,逻辑是:5 次/30 秒这个频率基本能排除偶发抖动(比如单次网络毛刺),确实是持续故障;60 秒的熔断时长足够让大多数服务商侧的临时问题(比如限流窗口重置、短暂过载)自愈,又不至于让业务方长时间感知不到这个渠道的存在。如果你的上游故障通常恢复得更快(比如历史数据显示 90% 的故障 20 秒内自愈),可以把熔断时长调短;反之如果上游一故障往往是大几分钟起步,调太短的熔断只会造成”熔断-恢复-立刻再熔断”的抖动,白白浪费半开探测请求的 token。
主动探活也要算成本:如果探活请求本身要跑一次完整的模型推理,哪怕只有几十个 token,量大了也是一笔真金白银的开销。经济一点的做法是探活用极短的 prompt(比如让模型只回复一个字),或者干脆探测一个不计费的健康检查端点(如果服务商提供的话),而不是拿正常业务请求去当探针。
生产配置建议
渠道数量:建议至少配置 2 个同模型上游(主 + 备),核心模型配置 3 个以上。
权重调优:初期用相等权重,根据各上游的实际错误率和延迟监控数据逐步调整。
分层设计:
同一模型,多账号负载均衡(提升吞吐)
└── 不同模型,优先级/failover(能力兜底)
监控指标:重点关注各上游的 QPS、P95 延迟、错误率、429 频率,及时发现失衡。
上完线之后你应该看到什么:配置好多上游负载均衡,别只看”没报错”就算完事,花五分钟对着监控面板核对几件事——第一,各上游的请求数分布是不是接近你设的权重比例(比如 3:2 的权重,实际观察应该在 55%~65% : 35%~45% 这个区间浮动,如果偏差特别大,多半是权重没生效或者某个上游一直在被熔断);第二,429 的频率是不是比单上游时期明显下降;第三,如果人为把某个上游断网模拟故障,观察业务是否真的能在熔断阈值设定的时间窗口内切走、不产生用户可感知的报错。这三项都对得上,才算这套负载均衡真正跑起来了,而不是配置写在那里但从没被验证过。
策略怎么选,给一个简单的决策表:
| 你的情况 | 推荐策略 |
|---|---|
| 多个渠道能力/配额完全一致 | 轮询或相等权重的加权轮询 |
| 渠道配额、速率上限不同 | 加权轮询,按各自 RPM 上限的比例定权重 |
| 请求时长差异大(有的秒回,有的长文本生成) | 最少连接数,且尽量用”加权最少连接” |
| 有官方 + 备用两级渠道,想优先用官方 | 优先级/failover,配合恢复观察期 |
| 大多数团队实际的做法 | 权重 + 优先级叠加:同级渠道内加权轮询分流,跨级之间走优先级兜底 |
成本也要算进去:假设上游 A 是官方直连(单价 100%),上游 B 是有折扣的渠道(单价打 8 折但 RPM 只有 A 的三分之一),单纯为了省钱把大量权重压给 B,很可能会先把 B 的限速打满,触发降级或排队,反而拖累整体响应速度。更合理的思路是给 B 设一个和它 RPM 上限匹配的权重上限,超出这个上限的流量还是要靠 A 兜底——省钱和稳定之间要按实际 SLA 要求找平衡点,别单纯冲着单价最低的渠道使劲堆权重。
常见问题
OneAPI 和 NewAPI 的负载均衡配置在哪里? 在”渠道管理”页面,同一模型可以添加多个渠道,设置各渠道的优先级和权重。OneAPI 用数字权重,数值越高优先级越高;权重相同时随机选取。
负载均衡会影响流式响应(streaming)吗? 不影响。网关在建立连接、选定上游后才开始转发流式数据,整个 SSE 流归属同一个上游连接,中途不会切换。
如果两个上游的模型版本不完全一致怎么办? 建议在渠道名称中标注版本,并在路由规则中区分。避免把不同能力的上游混入同一负载均衡池,否则输出质量会有波动。
延伸阅读:
- 多模型聚合 API 完整指南
- 模型路由策略:按成本/能力/延迟如何选
- 容灾与自动降级 failover
- 更多聚合 API 内容见 聚合API 专题
不想自己管负载均衡?申请力达云聚合 API 内测,多上游冗余开箱即用,无需运维。