本文档描述 Codex2API 的整体架构设计、核心组件及数据流。
┌─────────────────────────────────────────────────────────────────────────────┐
│ 客户端 (Client) │
│ - Web 浏览器 (/admin) │
│ - API 调用 (/v1/*) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 入口层 (Ingress) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Nginx / 负载均衡器 │ │
│ │ - SSL 终止 │ │
│ │ - 静态资源缓存 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Codex2API 服务 (Go + Gin) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ HTTP 路由层 │ │
│ │ ├── /admin/* → React SPA (go:embed) │ │
│ │ ├── /api/admin/* → 管理 API (admin.Handler) │ │
│ │ ├── /v1/* → 公共 API (proxy.Handler) │ │
│ │ └── /health → 健康检查 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────┼─────────────────────────────────┐ │
│ │ 中间件层 (Middleware) │ │ │
│ │ ┌────────────────┐ ┌───────────┐ │ ┌────────────────────────────┐ │ │
│ │ │ Recovery │ │ Logger │ │ │ API Key Auth │ │ │
│ │ │ ( panic 恢复 ) │ │ (请求日志) │ │ │ (公共 API 鉴权) │ │ │
│ │ └────────────────┘ └───────────┘ │ └────────────────────────────┘ │ │
│ │ ┌────────────────┐ ┌───────────┐ │ ┌────────────────────────────┐ │ │
│ │ │ Admin Auth │ │ RPM Limit │ │ │ CORS (开发环境) │ │ │
│ │ │ (管理 API 鉴权) │ │ (全局限流) │ │ │ │ │ │
│ │ └────────────────┘ └───────────┘ │ └────────────────────────────┘ │ │
│ └────────────────────────────────────┼─────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────▼─────────────────────────────────┐ │
│ │ 业务逻辑层 │ │
│ │ ┌──────────────────────┐ ┌──────────────────────────────────────┐ │ │
│ │ │ 管理后台逻辑 │ │ 代理转发逻辑 │ │ │
│ │ │ (admin.Handler) │ │ (proxy.Handler) │ │ │
│ │ │ • 账号 CRUD │ │ │ │ │
│ │ │ • 用量统计 │ │ • 账号选择 (auth.Store) │ │ │
│ │ │ • 系统设置 │ │ • 请求翻译 (translator) │ │ │
│ │ │ • 代理池管理 │ │ • 上游请求 (executor) │ │ │
│ │ └──────────────────────┘ │ • 流式处理 (SSE) │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────▼─────────────────────────────────┐ │
│ │ 数据访问层 │ │
│ │ ┌──────────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ Database │ │ Cache │ │ Token Store │ │ │
│ │ │ (PostgreSQL/ │ │ (Redis/ │ │ (内存账号池状态) │ │ │
│ │ │ SQLite) │ │ Memory) │ │ │ │ │
│ │ │ │ │ │ │ • 调度评分 │ │ │
│ │ │ • Accounts │ │ • Session │ │ • 并发计数 │ │ │
│ │ │ • Usage Logs │ │ • Rate Limit │ │ • 健康状态 │ │ │
│ │ │ • Settings │ │ │ │ • 冷却时间 │ │ │
│ │ └──────────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 上游服务 (Codex/OpenAI) │
│ - api.openai.com │
│ - api.codex.openai.com │
└─────────────────────────────────────────────────────────────────────────────┘
type Handler struct {
store *auth.Store // 账号池
configKeys map[string]bool // 静态 API Keys
db *database.DB // 数据库
dbKeys map[string]bool // 动态 API Keys 缓存
dbKeysUntil time.Time // 缓存过期时间
}职责:
/v1/chat/completions- Chat Completions 接口/v1/responses- Responses 接口/v1/models- 模型列表- API Key 认证
- 请求翻译 (OpenAI → Codex)
- 响应翻译 (Codex → OpenAI)
type Handler struct {
store *auth.Store // 账号池
cache cache.TokenCache // 缓存
db *database.DB // 数据库
rateLimiter *proxy.RateLimiter // 限流器
cpuSampler *cpuSampler // CPU 采样器
// ... 其他字段
}职责:
- 账号管理 (CRUD、导入导出)
- 用量统计与日志
- 系统设置管理
- 代理池管理
- 运维监控数据
type Store struct {
accounts []*Account // 写侧账号列表
accountSnapshot atomic.Pointer[accountListSnapshot] // 请求只读快照
fastScheduler atomic.Pointer[FastScheduler] // 分优先级/健康层索引
schedulerEngine atomic.Value // legacy/shadow/indexed
availability atomic.Pointer[availabilityHub]
maxConcurrency int64
}
type Account struct {
DBID int64 // 数据库 ID
RefreshToken string // 刷新令牌
AccessToken string // 访问令牌
Email string // 邮箱
PlanType string // 套餐类型
// 运行时状态
activeReqs int64 // 活跃请求数
totalReqs int64 // 总请求数
healthTier HealthTier // 健康层级
schedulerScore float64 // 调度分数
// 同步机制
mu sync.RWMutex // 读写锁
}调度流程:
┌─────────────────────────────────────────────────────────────┐
│ 账号选择流程 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. 过滤阶段 │
│ • 排除 Status = error 的账号 │
│ • 排除 HealthTier = banned 的账号 │
│ • 排除处于冷却期的账号 │
│ • 排除 AccessToken 为空的账号 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 评分阶段 │
│ • 计算健康层级 (healthy/warm/risky/banned) │
│ • 计算调度分数 (100 基线 + 各种惩罚/奖励) │
│ • 计算动态并发限制 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 选择阶段 │
│ • 排除已达并发上限的账号 │
│ • 先按调度优先级排序 │
│ • 同优先级按健康层级和调度分数排序 │
│ • Indexed 在最高优先级/健康层内使用游标或亲和哈希 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 使用阶段 │
│ • 增加活跃请求计数 │
│ • 执行上游请求 │
│ • 减少活跃请求计数 │
│ • 报告成功/失败 │
└─────────────────────────────────────────────────────────────┘
大号池执行模型:
- 账号集合在写侧变更后发布不可变指针快照;请求读快照不再复制
[]*Account。 indexed按调度优先级和健康层分桶,稳态通常只检查一个候选;稀疏 API Key/分组规则按需建立有界子池缓存,密集规则直接复用全局索引。- 账号并发释放、冷却恢复、outbox 变更通过 generation channel 广播。等待账号的请求订阅事件并重试,不使用固定间隔轮询全池。
- 数据库触发器把会改变路由的账号、API Key、分组、代理和设置写入
scheduler_outbox。每个实例按水位批量合并并增量更新内存投影,选号 miss 不再触发请求级全库 reconcile。 - Grok 事实/目录维护使用
maintenance_jobs(job_kind, due_at, lease_until);工作进程只 claim 到期行。PostgreSQL 使用SKIP LOCKED,SQLite 使用事务写门,替代每 30 秒扫描全账号并解析多份 JSON。
legacy 保留旧扫描实现;shadow 由旧路径实际选号并对索引做 1/64 可用性抽样;indexed 才让索引结果成为权威。三个模式可在运行时切换,不改变 /v1/* 协议。
运维接口 /api/admin/ops/overview 的 scheduler 对象暴露选号耗时累计值、旧路径扫描账号数、等待/唤醒、路由子池缓存、shadow 差异,以及 outbox 水位、积压、延迟和错误数。
// ExecuteRequest 执行上游请求
func ExecuteRequest(ctx context.Context, account *auth.Account,
body []byte, sessionID, proxyURL string) (*http.Response, error)功能:
- TLS 指纹伪装
- User-Agent 随机化
- 代理支持(全局/账号/代理池)
- 连接池复用
- 受账号动态并发上限约束的持久 WebSocket 连接池
- SSE 流读取
如果上游 WS 在首个下游内容前返回 close 1009,执行器保留当前账号租约和已解析代理,最多用 HTTP 降级一次。该路径不会重新进入账号调度;已经输出内容后不会降级。
// TranslateRequest OpenAI Chat → Codex Responses
func TranslateRequest(body []byte) ([]byte, error)
// TranslateStreamChunk Codex SSE → OpenAI SSE
func TranslateStreamChunk(data []byte, model, chunkID string) ([]byte, bool)转换内容:
messages→inputmax_tokens/temperature→ 删除(Codex 不支持)reasoning_effort→reasoning.effort- Anthropic
/v1/messages在无可用 Claude OAuth 账号时的speed:"fast"→ Codexservice_tier:"priority"(Anthropic 入参service_tier为 Priority Tier,不参与 fast mode 映射);Claude OAuth 账号优先走原生 Anthropic Messages 透传,不进入 Codex 转换链 - SSE 事件类型转换
┌─────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────▶│ Router │────▶│ Middleware │────▶│ Handler │
└─────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────────────────────────────────────────┼──────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌─────────┐
│ Account Store│ │ Executor │ │ Usage │
│ (Select) │────────────────────────▶│ (Upstream) │ │ Logger │
└──────────────┘ └──────┬───────┘ └─────────┘
│
▼
┌──────────────┐
│ Codex │
│ API │
└──────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 账号状态机 │
│ │
│ ┌──────────┐ │
│ │ Initial │ │
│ └────┬─────┘ │
│ │ Import/Add │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ready │────▶│ cooldown │────▶│ ready │ │
│ └────┬─────┘ └──────────┘ └──────────┘ │
│ │ (429/401 冷却) (冷却结束) │
│ │ │
│ │ Error │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ error │────▶│ deleted │ │
│ └──────────┘ └──────────┘ │
│ (软删除) │
└─────────────────────────────────────────────────────────────────────────┘
// 冷却类型与时长
type CooldownRule struct {
RateLimited time.Duration // 429 限流: 从响应头解析或按套餐推断
Unauthorized time.Duration // 401 未授权: 5分钟 ~ 24小时
Timeout time.Duration // 超时: 15分钟
ServerError time.Duration // 5xx 错误: 15分钟
}
// 套餐类型冷却策略
const (
FreeCooldown = 7 * 24 * time.Hour // Free 套餐 429: 7天
TeamCooldown5h = 5 * time.Hour // Team 5h 窗口用完: 5小时
TeamCooldown7d = 7 * 24 * time.Hour // Team 7d 窗口用完: 7天
)type HealthTier int
const (
Healthy HealthTier = iota // 健康
Warm // 温热(近期有小问题)
Risky // 风险(近期有大问题)
Banned // 封禁
)层级判定:
| 层级 | 条件 | 动态并发 |
|---|---|---|
| Healthy | 无近期错误 | MaxConcurrency |
| Warm | 有轻微错误 | MaxConcurrency / 2 |
| Risky | 有严重错误 | 1 |
| Banned | 401 未授权 | 0 |
type SchedulerScore struct {
BaseScore float64 = 100
// 惩罚项 (P)
UnauthorizedPenalty float64 // 401: -50, 24h 衰减
RateLimitPenalty float64 // 429: -22, 1h 衰减
TimeoutPenalty float64 // 超时: -18, 15min 衰减
ServerPenalty float64 // 5xx: -12, 15min 衰减
FailurePenalty float64 // 连续失败: -6/次, 最多 -24
UsagePenalty7d float64 // 7d 用量: ≥70% -8, ≥100% -40
LatencyPenalty float64 // 延迟: ≥5s -4, ≥20s -15
SuccessRatePenalty float64 // 成功率: <75% -8, <50% -15
// 奖励项 (B)
SuccessBonus float64 // 连续成功: +2/次, 最多 +12
}
// 最终分数 = BaseScore + ΣB - ΣPfunction SelectAccount():
candidates = []
for account in accounts:
// 1. 基础过滤
if account.Status == "error": continue
if account.HealthTier == Banned: continue
if account.IsInCooldown(): continue
if account.AccessToken == "": continue
// 2. 计算动态限制
concurrencyLimit = calculateDynamicLimit(account.HealthTier)
if account.ActiveRequests >= concurrencyLimit: continue
// 3. 计算调度分
account.SchedulerScore = calculateScore(account)
candidates.append(account)
if candidates is empty:
return null
// 4. 排序
sort candidates by:
- HealthTier (Healthy > Warm > Risky)
- SchedulerScore (desc)
- ActiveRequests (asc)
// 5. 随机打散 (15% 概率)
if random() < 0.15 and len(candidates) > 1:
return candidates[random(0, min(3, len(candidates)))]
return candidates[0]
-- 账号表
CREATE TABLE accounts (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
status VARCHAR(50) DEFAULT 'ready',
credentials JSONB DEFAULT '{}', -- email, refresh_token, access_token, etc.
proxy_url VARCHAR(500),
cooldown_until TIMESTAMP,
cooldown_reason VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
deleted_at TIMESTAMP -- 软删除
);
-- 用量日志表
CREATE TABLE usage_logs (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT REFERENCES accounts(id),
account_email VARCHAR(255),
endpoint VARCHAR(100),
model VARCHAR(100),
status_code INTEGER,
duration_ms INTEGER,
first_token_ms INTEGER,
prompt_tokens INTEGER DEFAULT 0,
completion_tokens INTEGER DEFAULT 0,
total_tokens INTEGER DEFAULT 0,
reasoning_effort VARCHAR(20),
stream BOOLEAN DEFAULT FALSE,
service_tier VARCHAR(20),
created_at TIMESTAMP DEFAULT NOW()
);
-- 用量小时汇总:按 UTC 整点小时 + API Key/渠道/模型/端点/状态码/流式/内部请求聚合 usage_logs。
-- 写日志的事务里同步累加,清空日志时一并清空;升级前的历史在后台按 id 分块回填。
-- 用量页、自助用量页的长区间统计读整点小时的汇总,只有首尾零头扫明细。
CREATE TABLE usage_log_hourly (
bucket TIMESTAMPTZ NOT NULL,
api_key_id BIGINT, api_key_name TEXT, api_key_masked TEXT, channel TEXT,
model TEXT, effective_model TEXT, inbound_endpoint TEXT, endpoint TEXT,
status_code INTEGER, stream BOOLEAN, internal INTEGER,
requests BIGINT, total_tokens BIGINT, user_billed DOUBLE PRECISION, ...,
PRIMARY KEY (bucket, api_key_id, api_key_name, api_key_masked, channel, model,
effective_model, inbound_endpoint, endpoint, status_code, stream, internal)
);
-- 系统设置表
CREATE TABLE system_settings (
id INTEGER PRIMARY KEY DEFAULT 1,
max_concurrency INTEGER DEFAULT 2,
global_rpm INTEGER DEFAULT 0,
test_model VARCHAR(100) DEFAULT 'gpt-5.5',
test_concurrency INTEGER DEFAULT 50,
proxy_url VARCHAR(500),
pg_max_conns INTEGER DEFAULT 50,
redis_pool_size INTEGER DEFAULT 30,
response_cache_local_max_bytes BIGINT NOT NULL DEFAULT 67108864,
response_cache_local_max_entry_bytes BIGINT NOT NULL DEFAULT 8388608,
response_cache_reconstruct_max_bytes BIGINT NOT NULL DEFAULT 67108864,
response_cache_config_generation BIGINT NOT NULL DEFAULT 1,
auto_clean_unauthorized BOOLEAN DEFAULT FALSE,
auto_clean_rate_limited BOOLEAN DEFAULT FALSE,
admin_secret VARCHAR(255)
);
-- API Keys 表
CREATE TABLE api_keys (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
key_hash VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- 代理池表
CREATE TABLE proxies (
id BIGSERIAL PRIMARY KEY,
url VARCHAR(500) NOT NULL,
label VARCHAR(255),
enabled BOOLEAN DEFAULT TRUE,
last_tested_at TIMESTAMP,
last_test_result VARCHAR(50),
latency_ms INTEGER
);
-- 账号事件表(用于趋势分析)
CREATE TABLE account_events (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT REFERENCES accounts(id),
event_type VARCHAR(50), -- added, deleted, status_changed
event_data JSONB,
created_at TIMESTAMP DEFAULT NOW()
);| 数据 | 缓存策略 |
|---|---|
| API Key 鉴权配置 | 默认 L1 15 秒 / Redis L2 5 分钟,按数据库作用域和事务修订号隔离;关闭两级缓存后使用旧 api-key 策略 |
| API Key 请求数、费用、Token 窗口统计 | 数据库聚合后写入 api-key-limits,TTL 60 秒;多个窗口批量读取 |
| Key × 账号的共享用量增量 | api-key-scope-delta 分钟桶,TTL 5 分钟;最近三个桶共用 5 秒本地读取快照 |
| Access Token | 专用 Token 缓存;写入 TTL 根据凭证有效期确定 |
| 会话亲和关系 | 本地绑定及共享缓存;受会话 TTL 和账号可用性控制 |
| 账号、模型冷却 | 运行态缓存;TTL 与冷却结束时间对应 |
运行态 Redis key 使用命名空间和摘要构造,业务 key 不直接作为 Redis key 输出。API Key 的 RPM/RPD 当前读取用量聚合快照;严格模型周预算由数据库事务和请求幂等记录控制。
API Key 运行态读取提供两个可选批量接口:RuntimeBatchReader 用于 JSON 窗口快照(Redis MGET),RuntimeCounterBatchReader 用于共享用量哈希(Redis Pipeline HGETALL)。每个 Redis 批次最多 128 个键;旧 TokenCache 适配器可以继续使用逐项读取。Memory 驱动在锁内复制快照,调用方不会拿到可修改底层缓存的引用。
鉴权配置读取先复核数据库修订号(每进程最多复用 250 毫秒),再依次查询 L1、Redis L2 和数据库。配置回源按 Key/本地代际合并;L1 结果给每个调用者复制可变字段。累计 quota_used 不进入快照,有累计额度的 Key 仍逐次查询数据库;模型周预算保留转发前的权威事务。
api_key_auth_cache_state 的全局修订号与 Key 配置变更在同一事务提交,使用行更新保证提交顺序;不以可能乱序提交的 outbox 自增 ID 最大值作为版本。用量更新不推进版本。Redis key 包含数据库作用域、修订号和 Key 摘要,旧请求延迟回填只能写入旧版本。本地代际在失效时同步推进,阻止在途查询重新放入 L1。管理操作同步失效并发送 Pub/Sub;定期复核持久修订号覆盖通知断连、丢失和旧版本写入。
L1 受 4,096 条、16 MiB 逻辑 JSON 字节和单条 64 KiB 预算限制;大条目仍可从数据库服务当前请求,但不入缓存。Memory 模式不复制第二份 L2。配置回源与修订查询都可由服务关闭取消并等待退出;单个请求取消不会中断其他调用者共享的读取。鉴权 Redis 读写有 300 毫秒 socket/上下文预算,故障回源数据库;无法复核数据库修订号时返回 503。
分组/账号预算使用独立的回源合并,同一批读取不因首个调用者取消而中断其他请求;共享读取仍有两秒上限。后台共享增量写入限制为 16 个槽位,饱和时同步回退。
previous_response_id 连续请求使用一层每进程独立的有界 L1。完成响应时先按完整 call/output 组保留尾部最多 200 个 raw item,再写入 L1;默认总量 64 MiB、单条准入 8 MiB、最多 2,000 条、绝对 TTL 45 分钟。LRU 同时受条数和逻辑字节预算约束,命中不会延长绝对 TTL。
完成响应
│
├─ pair-safe 尾部裁剪(最多 200 items)
│
├─ 本进程 L1(64 MiB total / 8 MiB entry / 2,000 entries / 45m)
│
└─ Redis 共享 response context(仅 Redis 模式)
previous_response_id 连续请求
│
├─ L1 命中 ───────────────────────────────▶ 重建上下文
│
└─ L1 未命中
├─ Redis 有界读取 → 规范化 → 重建上限校验
│ ├─ 符合 L1 准入 → 服务请求并提升到 L1
│ └─ 超 L1、未超重建上限 → 仅服务本次请求,不提升
└─ Memory 模式 → 无共享 response context 后备
三个数据库预算分别控制 L1 总量、L1 单条准入和共享后端重建上限。只有预算实际变化的事务提交才分配递增 generation,并立即应用到写入实例;同值或空更新不递增。其他实例每 5 秒读取一次,单次最多等待 3 秒,只应用更新的 generation。读取失败时保留最后一次有效配置,并记录同步错误供运维页展示。
Memory 缓存配合 SQLite 文件主库时默认以本地磁盘持久层(cache/durable_response_context.go)作为 L2:L1 淘汰、超过 L1 准入预算或进程重启后仍从磁盘恢复,原生 WebSocket 共享来源记录仍只在进程内。关闭持久层后只有 L1;已知超限/淘汰,或依赖的必需上下文缺失/过期且没有 relay 后备时,Responses/Compact 连续请求可返回 HTTP 409 previous_response_not_found。Redis 值损坏或超过重建上限且没有 relay 后备时同样返回 409;共享后端传输故障且请求确实依赖该上下文时可返回 HTTP 503。Redis 中未超过重建上限的上下文不会仅因超过 L1 准入预算而失败。
客户端原生 Responses WebSocket 在健康连接上保留 previous_response_id,向上游发送当轮增量;原连接丢失后的恢复仍依赖完整的本地或共享快照,受上述预算、完整性和 409/503 错误语义约束。HTTP Responses/Compact 的原生 Codex 路径必须在当前归属内重建父引用;原生 OpenAI Responses relay 可保留父 ID 交给自己的上游恢复。
GET /api/admin/ops/overview 的 response_cache 从一个锁内快照返回 effective/applied generation、当前/上限/高水位/最大观测条目的逻辑字节,以及本地/远程命中与未命中、过期、条数/字节淘汰、超限旁路、Memory 超限拒绝和最终 409 计数。这里的字节是保留 JSON payload 长度之和,不包含 Go 对象、LRU、allocator 或容器开销,不是 RSS 硬上限。进程内存需要结合 Linux/Docker RSS、Go HeapAlloc / HeapInuse / HeapReleased 和 NumGC 判断。
┌─────────────────────────────────────────────────────────────┐
│ 认证层级 │
├─────────────────────────────────────────────────────────────┤
│ Level 1: 传输层 │
│ • HTTPS/TLS 加密 │
│ • HSTS 头部 │
├─────────────────────────────────────────────────────────────┤
│ Level 2: API 认证 │
│ • API Key Bearer Token │
│ • 5分钟缓存,减少 DB 查询 │
├─────────────────────────────────────────────────────────────┤
│ Level 3: Admin 认证 │
│ • X-Admin-Key 请求头 │
│ • 支持环境变量或数据库配置 │
│ • 所有管理操作审计日志 │
└─────────────────────────────────────────────────────────────┘
// Token 存储
func StoreToken(db *DB, accountID int64, token string) {
// 数据库只存储加密后的 token
encrypted := encrypt(token, encryptionKey)
db.Exec("UPDATE accounts SET credentials = jsonb_set(credentials, '{access_token}', $1) WHERE id = $2",
encrypted, accountID)
// 同时缓存到 Redis(加密存储)
redis.Set(ctx, fmt.Sprintf("token:%d", accountID), encrypted, 30*time.Minute)
}
// 日志脱敏
func SanitizeLog(msg string) string {
// 隐藏 token 详情
msg = regexp.MustCompile(`token=[^\s]+`).ReplaceAllString(msg, "token=***")
msg = regexp.MustCompile(`Bearer [^\s]+`).ReplaceAllString(msg, "Bearer ***")
return msg
}| 层级 | 机制 | 说明 |
|---|---|---|
| 全局 | RPM 限流 | 基于令牌桶算法 |
| API Key | RPM/RPD/费用/Token 窗口 | 数据库聚合 + 60 秒统计缓存;RPM/RPD 另有进程内准入计数 |
| 下游 WS | 连接数 | 每 Key 64 条、全局 4096 条、匿名每 IP 16 条 |
| 账号 | 并发限制 | 动态调整 |
| IP | 连接限制 | 防止单 IP 过多连接 |
| 应用 | 请求大小 | 限制请求体大小 |
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡器 │
│ (Nginx / HAProxy) │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Codex2API #1 │ │ Codex2API #2 │ │ Codex2API #3 │
│ │ │ │ │ │
│ + PostgreSQL │ │ + PostgreSQL │ │ + PostgreSQL │
│ + Redis │ │ + Redis │ │ + Redis │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└──────────────┼──────────────┘
▼
┌─────────────────┐
│ 共享 Redis │
│ (Session/锁) │
└─────────────────┘
| 组件 | 当前实现 | 可替换为 |
|---|---|---|
| 数据库 | PostgreSQL | MySQL, TiDB |
| 缓存 | Redis | Memcached, 本地缓存 |
| HTTP | Gin | Echo, Fiber |
| 前端 | React | Vue, Svelte |