调度、粘性与故障切换
调度器的目标不是简单轮询,而是在权限、模型能力、账号健康、会话连续性和负载之间做选择。
调度输入
一次选择至少包含:
- group ID。
- 目标平台。
- requested model 和映射后的模型。
- session hash / previous response ID。
- 请求类型:文本、图片、视频、compact 等。
- 已失败账号集合。
- 客户端和账号类型限制。
候选来源
优先从调度快照读取账号。快照不可用时,可以按配置受控回源 PostgreSQL,并限制超时和 QPS,避免 Redis 故障把数据库打满。
快照通过管理写路径、outbox、周期重建等方式保持新鲜。
硬过滤
账号满足全部必要条件才能成为候选:
- 属于分组或合法 fallback。
- 平台与路由兼容。
- status active 且 schedulable。
- 未过期。
- 支持 requested/upstream model。
- 不在本次 failed account IDs。
- 不在临时限流、过载或错误冷却。
- 满足 OAuth/API Key/privacy 等类型规则。
- 请求能力允许,例如图片、compact、WebSocket。
无候选时,错误分类会区分 model not found、容量不足和所有账号不可用。
粘性会话
粘性会话优先复用已经绑定的账号:
request metadata
+ API Key
+ client/IP hints
│
▼
session hash
│ Redis lookup
▼
bound account ID粘性命中后仍要重新验证账号是否可调度、支持模型和有并发空间。
为什么需要 sticky escape
如果绑定账号错误率高、TTFT 很慢或负载过高,严格粘性会让一个会话持续受损。OpenAI 调度器可以根据 TTFT、错误率等条件逃离粘性账号,重新选择健康候选。
这在“会话连续性”和“实时可用性”之间做权衡。previous response 强依赖特定账号或连接时,逃离成本更高。
评分维度
OpenAI 调度器的可配置权重包括:
- priority。
- load。
- queue。
- error rate。
- TTFT。
- quota reset。
- quota headroom。
- previous response。
- session sticky。
并不是所有平台都使用完全相同的评分器。通用 Gateway 仍可能采用优先级、负载率、窗口重置和 LRU 等策略。
Top-K 与随机性
只选全局最高分容易让同一账号形成热点。调度器可以从 Top-K 候选中选择,结合粘性和负载减少抖动。
如果优先级差异很大,低优先级账号可能长期不被使用;如果所有账号同优先级,负载和重置时间会成为主要因素。
Prefer Soonest Reset
开启后会优先使用额度窗口即将重置的账号,先消耗“快过期”的剩余额度,把距离重置较远的账号留作后备。
适合 use-it-or-lose-it 的订阅额度,不适合希望平均磨损所有账号的场景。
账号并发槽位
选择候选不等于已经获得账号。调度器或 Handler 会获取 Redis 账号槽位:
- 成功:携带 release func 继续。
- 满载:等待、换账号或失败。
- context 取消:退出并清理等待计数。
某些选择结果已经预先获取槽位,调用方必须识别 Acquired 和 ReleaseFunc,避免重复获取或泄漏。
Failover State
一次请求维护:
- 已失败账号 ID 集合。
- 切换次数。
- 最后一个上游错误。
- 是否存在强粘性绑定。
- 是否强制按缓存计费。
- 单账号重试状态。
每次失败先分类,再决定:同账号重试、切换候选、fallback group 或终止。
错误分类
适合切换
- 连接失败、DNS、TLS、代理错误。
- 上游 429/529/过载。
- OAuth token 无效且刷新失败。
- 部分平台内部错误。
- 账号模型临时容量不足。
通常不适合切换
- 请求 JSON 不合法。
- 模型名本身不存在。
- 内容审核拒绝。
- 用户余额或权限不足。
- 流式内容已经发送。
400 是否切换由配置控制。开启前要确认上游 400 确实代表账号差异,而不是所有账号都会失败的客户端错误。
Fallback Group
Fallback group 是跨分组降级,不等同于账号 failover。进入 fallback 时需要重新处理:
- 新分组权限与平台。
- 计费资格和倍率。
- 模型映射。
- 候选账号和并发。
配置循环 fallback 会造成复杂行为,应保证 fallback 图无环。
单账号分组
单账号分组无法真正切换账号。部分平台会允许同账号短暂重试,并避免第一次容量错误就设置过长模型冷却。
如果业务要求高可用,单账号分组本身就是单点,不应依赖重试掩盖。
调度排障
看到“无可用账号”时,按集合逐步缩小:
分组全部账号
→ active + schedulable
→ 未过期
→ 支持模型/请求类型
→ 未限流/过载
→ 满足账号类型与 privacy
→ 有账号并发槽位运维页面的 scheduler score、候选数量和排除原因比只看账号 status 更有价值。