feat: 余额预扣 reserve/capture、冷却兜底重试、Prometheus /metrics
- 预扣:每请求按 body 估算成本上界计入冻结额(1e-8 USD 原子计数),402 判定 改为「余额 - 在途冻结 ≤ 0」,并发的长流式请求不再互相透支;OT_PROXY_RESERVE_MAX_TOKENS 控制估算基数(默认 4096,0=关闭),handler defer 释放,失败/中断/panic 均覆盖 - 调度:全部候选都在运行期冷却时,剩余冷却 ≤ OT_PROXY_COOLDOWN_GRACE 的最早恢复渠道 兜底重试一次而不是 503(429 Retry-After 语义);DB 层不可用的渠道不参与兜底 - 指标:openteam_proxy_requests_total / request_duration / channel_failures / usage_queue_depth / frozen_balance,/metrics 默认关闭(OT_METRICS_ENABLED 开启)
This commit is contained in:
1 parent
b2e500ff51
commit
0e10f0bbde
14 files changed
+493
-36
No files matched your search
@@ -31,6 +31,9 @@ type Config struct {
|
||||
// 反向代理列表(CIDR 或 IP)。只有配置它,gin 才信任 X-Forwarded-For 取客户端 IP,
|
||||
// 认证限速依赖这个 IP;直连部署留空即可。
|
||||
TrustedProxies []string
|
||||
|
||||
// MetricsEnabled 是否暴露 Prometheus /metrics 端点(会泄露模型/渠道名与流量,默认关)。
|
||||
MetricsEnabled bool
|
||||
}
|
||||
|
||||
// UsageConfig 用量数据的保留策略。
|
||||
@@ -90,7 +93,13 @@ type ProxyConfig struct {
|
||||
FailoverBudget time.Duration // 故障转移给"第二个及以后的渠道"留的时间预算,0=不限
|
||||
// FailoverSwitchCaps 按失败类型(net|server|rate|quota)限制换渠道次数,0=不限。
|
||||
// 429/配额类失败瞬时返回,时间预算对它们形同虚设,按次数封顶才有意义。
|
||||
FailoverSwitchCaps map[string]int
|
||||
FailoverSwitchCaps map[string]int
|
||||
// CooldownGrace 全部候选渠道都在运行期冷却时,最早恢复者剩余冷却 ≤ 此值就兜底
|
||||
// 重试一次而不是 503(429 的 Retry-After 语义)。0=关闭。
|
||||
CooldownGrace time.Duration
|
||||
// ReserveMaxTokens 预扣(reserve/capture)估算的最大输出 token,0=关闭。
|
||||
// 每请求按 body 估算成本上界计入冻结额,402 判定改为「余额 - 在途冻结 ≤ 0」。
|
||||
ReserveMaxTokens int
|
||||
StreamHeartbeat time.Duration // 流式空闲多久发一次 SSE 注释心跳,0=关闭
|
||||
FirstTokenWait time.Duration // 流式放行前等内容的最长时间,0=不限;超时未出内容换下一个渠道
|
||||
AffinityTTL time.Duration // 会话亲和 TTL,0=关闭;同一会话粘住上次应答的渠道以命中上游缓存
|
||||
@@ -166,6 +175,9 @@ func Load() (*Config, error) {
|
||||
v.SetDefault("proxy.failover_caps.server", 3)
|
||||
v.SetDefault("proxy.failover_caps.rate", 2)
|
||||
v.SetDefault("proxy.failover_caps.quota", 3)
|
||||
v.SetDefault("proxy.cooldown_grace", "30s")
|
||||
v.SetDefault("proxy.reserve_max_tokens", 4096)
|
||||
v.SetDefault("metrics.enabled", false)
|
||||
v.SetDefault("proxy.stream_heartbeat", "15s")
|
||||
v.SetDefault("proxy.first_token_wait", "0s")
|
||||
v.SetDefault("proxy.affinity_ttl", "5m")
|
||||
@@ -226,6 +238,8 @@ func Load() (*Config, error) {
|
||||
"rate": v.GetInt("proxy.failover_caps.rate"),
|
||||
"quota": v.GetInt("proxy.failover_caps.quota"),
|
||||
},
|
||||
CooldownGrace: v.GetDuration("proxy.cooldown_grace"),
|
||||
ReserveMaxTokens: v.GetInt("proxy.reserve_max_tokens"),
|
||||
StreamHeartbeat: v.GetDuration("proxy.stream_heartbeat"),
|
||||
FirstTokenWait: v.GetDuration("proxy.first_token_wait"),
|
||||
AffinityTTL: v.GetDuration("proxy.affinity_ttl"),
|
||||
@@ -253,6 +267,7 @@ func Load() (*Config, error) {
|
||||
AdminPassword: v.GetString("admin_password"),
|
||||
CORSAllowOrigins: splitList(v.GetString("cors.allow_origins")),
|
||||
TrustedProxies: splitList(v.GetString("server.trusted_proxies")),
|
||||
MetricsEnabled: v.GetBool("metrics.enabled"),
|
||||
}
|
||||
if err := cfg.validate(); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in new issue
Block a user