核心概念
Cordy Gateway 的构成方式——控制平面与数据平面、模型解析链、通道与路由,以及请求生命周期。
本页解释你的客户端所调用的单一入口背后的各个部件。理解它们,就能清楚配置存放在何处、一个 model 名称如何变成真实的上游调用,以及一个请求从鉴权到计费经历了什么。
两个平面
Cordy Gateway 拆分为控制平面与数据平面,二者共享一套 PostgreSQL 数据库与一个 Redis 实例。
控制平面——Admin
- 它是什么: 一个 Django 应用(带 django-unfold 管理界面)。
- 默认端口:
8001。 - 拥有: 数据库 schema 与全部迁移。每张表——供应商、通道、模型、定价、用户、API 密钥、审计日志——都在此定义和演进。
- 用于: 注册供应商与通道、定义公开模型与定价、管理用户、签发与吊销 API 密钥、查看用量与审计记录。
运维人员与控制平面交互;客户端从不。
数据平面——Gateway
- 它是什么: 一个由 Granian ASGI 服务器承载的 Starlette 应用。它就是你客户端调用的、兼容 OpenAI 的 API。
- 默认端口:
8002。 - 数据访问: 通过连接池以原生
psycopg访问 PostgreSQL,请求路径上没有 Django。这让热路径保持精简、快速。 - 用于: 鉴权、作用域与配额校验、速率与并发限制、模型路由、故障转移、调用上游供应商,以及结算用量。
数据平面把 schema 视为以读为主的配置,读取控制平面写入的供应商、通道、模型、密钥与定价。
共享状态
- PostgreSQL 保存持久化配置与记录。schema 由 Admin 迁移拥有;Gateway 读取它。
- Redis 保存快速、临时、集群共享的状态:速率限制计数、配额预留、响应缓存、幂等键、通道冷却状态,以及把用量批量写回 Postgres 的写缓冲。与资金相关的键(已累计费用、用量缓冲)永不被驱逐。
因为 Redis 是共享的,运行多个网关副本是安全的:速率限制、配额、冷却与幂等全都是集群级的。扩展见部署。
模型解析链
当客户端发送 "model": "gpt-4o-mini" 时,这个名称是一个公开模型 slug——由你选择的稳定标识。网关通过一条记录链,把它解析为具体的上游调用:
PublicModel ──< ProviderModel ──< ChannelProviderModel >── Channel >── Provider
(公开 slug) (厂商模型名 (在某通道上启用 (路由目标 (上游厂商)
+ 定价) 某模型) + 凭据)
- PublicModel(公开模型)——客户端请求的公开身份(例如
gpt-4o-mini)。客户端只看到这些 slug;从不看到厂商模型名或上游密钥。 - ProviderModel(供应商模型)——把一个 PublicModel 映射到某个 Provider 的真实模型名(例如厂商字符串
gpt-4o-mini),并携带定价(input_price、output_price)与能力标志(supports_chat、supports_embeddings、supports_streaming、context_window)。 - Provider(供应商)——上游厂商集成(OpenAI、Anthropic、Azure、Bedrock 等),带
provider_type与状态。 - Channel(通道)——某 Provider 上的一个具体路由目标:一份上游凭据(以 Fernet 加密存储)、一个可选
base_url,以及诸如weight、priority、region、rate_limit、concurrent_limit、timeout_seconds与可选cost_weight等路由属性。 - ChannelProviderModel(通道-供应商模型绑定)——表示"此通道可服务此供应商模型"的绑定。一个通道只路由到一个供应商,且所绑定的供应商模型必须属于同一个供应商。
一个公开模型可由多个通道提供服务——同一供应商内或跨不同供应商——这正是加权均衡与故障转移得以实现的基础。
通道与路由
一个通道是抵达某模型的一条途径。由于单个公开模型可映射到多个通道,网关必须为每个请求择一,并在失败时回退。
选择通道
网关先筛选出能服务所请求模型的、健康的、符合等级的、处于活跃状态的通道,再依据路由策略择一:
| 策略 | 行为 |
|---|---|
weighted | 先按优先级排序,再在最高优先级带内加权随机。默认策略。 |
cheapest | 有效价格最低者优先,使用各通道的 cost_weight 乘子作用于供应商模型价格。 |
fastest | 观测 P95 时延最低者优先,取自进程内时延记录器;尚无数据时回退到 weighted。 |
region_aware | 优先选择区域与调用方区域匹配的通道,再在最优分桶内加权。 |
默认策略由 GATEWAY_DEFAULT_ROUTING_STRATEGY 设置(默认 weighted)。客户端可用 X-Routing-Strategy 请求头按请求覆盖。见供应商与客户端集成。
故障转移、冷却与熔断器
- 故障转移: 若所选通道以可重试错误失败,网关会为同一模型尝试下一个符合条件的通道。故障转移次数在
X-Failover-Count响应头中报告。 - 同通道重试: 瞬时错误会在同一通道上以指数退避重试,之后才启动故障转移。
- 冷却: 反复出错(或被上游限流)的通道可被置入冷却窗口,期间被跳过。冷却状态存于 Redis,跨副本共享。
- 熔断器: 每个 worker 跟踪供应商健康,对失败的供应商触发熔断,快速卸载负载。熔断器状态在指标中暴露。
请求生命周期
每个请求在抵达路由处理器之前,会流经一条有序的中间件链,随后进入路由与结算。各阶段依序为:
- 请求体大小守卫——超过
GATEWAY_MAX_BODY_BYTES(默认 10 MiB)的请求在任何缓冲之前即以413拒绝。 - 鉴权——从
Authorization: Bearer或X-API-Key提取wnx_密钥,以 SHA-256 哈希后查找(Redis 缓存、Postgres 后端)。无效、已吊销、未激活或已过期的密钥返回401。若鉴权基础设施本身不可用,网关返回带Retry-After的503(这是瞬时信号,而非凭据错误信号)。 - 作用域与配额——路径被映射到所需作用域(
chat、completions、embeddings、models、balance、admin)。缺少该作用域的密钥返回403 scope_denied。超出令牌配额的密钥返回429 quota_exceeded。一份配额预留会被置入 Redis 以放行该请求。 - 模型权限——按用户的模型访问规则被强制执行,因此可将某密钥限制在公开模型的子集内。
- 速率限制——每密钥每分钟请求数限制在 Redis 中强制执行;超限请求返回
429。 - 在途并发限制——每密钥并发请求上限为
GATEWAY_MAX_INFLIGHT_PER_KEY(默认 50);该槽位在整个流式生命周期内被占用,完成或客户端断开时释放。 - 护栏——可选的入站 PII 模式扫描(默认仅记录)。
- 区域推导——推导调用方区域(优先采用显式的
X-Region请求头),以供区域感知路由使用。 - 幂等——带
Idempotency-Key的 POST 受重放保护:TTL 内的重复请求返回已存响应,而不再次调用上游。 - 路由与故障转移——网关为该模型选择一个通道并调用上游供应商(经 LiteLLM 或直连的兼容 OpenAI 路径),按需跨通道故障转移。
- 计费与用量结算——计算令牌用量与成本,结算配额预留,扣减预付信用,写入用量记录。结算经 Redis 缓冲,由后台 worker 落盘至 Postgres。
成功响应会携带诸如 X-Channel-Id、X-Provider、X-Failover-Count、X-Routing-Overhead-Ms 等可观测性请求头。
各要素归属
| 关注点 | 归属 |
|---|---|
| 供应商、通道、模型、定价 | 控制平面(Admin),存于 Postgres |
| 用户、API 密钥、作用域、配额 | 控制平面(Admin),存于 Postgres |
| 审计日志与 RBAC | 控制平面(Admin) |
| 请求路由、鉴权强制、计量 | 数据平面(Gateway) |
| 速率限制、配额、缓存、冷却、幂等 | Redis(共享) |
| 上游凭据(静态加密) | Postgres 中的通道记录 |