File size: 2,780 Bytes
6d60378 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | // backoff.go 轮转退避与抖动的单一事实来源(WAF 403 修复 P0-2/P0-1 共用):
// 指数基数/封顶/抖动比例一处定义,chatCompletions 轮转退避与 WAF 软冷却基数
// (handler.wafCooldownBase)共用同一 jitterDur,不在 handler 与 upstream 各写一份。
package server
import (
"context"
"math/rand/v2"
"time"
)
// rotateBackoffBase 轮转退避基数(对齐官方 intl CLI computeRequestRetryDelayMs
// 的 500ms 形态,WAF 403 并发研究报告 §2.1/§6 P0-2)。测试可置 0 跳过等待
// (TestMain 已置 0 加速轮转测试;退避界断言测试临时恢复)。
var rotateBackoffBase = 500 * time.Millisecond
const (
// rotateBackoffCap 轮转退避封顶(报告 P0-2 建议 8s:轮转上限默认 3 次,
// 实际等待序列 500ms/1s,封顶只约束极端配置下的 MaxRotate)。
rotateBackoffCap = 8 * time.Second
// jitterFraction 抖动比例(±25%,对齐 intl CLI delay×(1±0.25) 形态)。
jitterFraction = 0.25
)
// jitterDur 给时长施加 ±jitterFraction 的均匀抖动,返回 [d·(1-f), d·(1+f)] 区间值。
// d<=0 原样返回(零等待不抖动)。抖动目的是打散多请求同相位重试(WAF 频控按
// 密度判罚,齐步走的退避会以固定周期再次聚团)。
func jitterDur(d time.Duration) time.Duration {
if d <= 0 {
return d
}
f := 1 + (rand.Float64()*2-1)*jitterFraction
out := time.Duration(float64(d) * f)
if out < 0 {
return 0
}
return out
}
// backoffAfter 返回第 n 次轮转(0 基:首次失败换号前 n=0)前应等待的退避时长:
// base·2^n 封顶 rotateBackoffCap,再施加 ±25% 抖动。base 置 0(测试)时恒 0。
// 用逐次翻倍而非位移:base 调整后无需同步维护移位上限,溢出由封顶比较兜底。
func backoffAfter(n int) time.Duration {
d := rotateBackoffBase
if d <= 0 {
return 0
}
for k := 0; k < n && d < rotateBackoffCap; k++ {
d *= 2
if d <= 0 { // 翻倍溢出成非正数:直接按封顶处理
return jitterDur(rotateBackoffCap)
}
}
if d > rotateBackoffCap {
d = rotateBackoffCap
}
return jitterDur(d)
}
// sleepCtx 可取消的等待:ctx 取消立即返回 false(客户端断连/优雅停机不必等退避
// 睡醒),等满返回 true。d<=0 立即放行。与 scheduler.sleepCtx 同模式(该函数未
// 导出且 scheduler 不宜被 server 反向依赖,按任务书「等价物」口径在消费侧实现)。
func sleepCtx(ctx context.Context, d time.Duration) bool {
if d <= 0 {
return ctx.Err() == nil
}
t := time.NewTimer(d)
defer t.Stop()
select {
case <-ctx.Done():
return false
case <-t.C:
return true
}
}
|