// 候选路由与冷却:凭证级(401/403)与绑定级(429/5xx/超时)冷却,粘性会话与故障迭代。 package route import ( "sync" "time" ) // 冷却时长:凭证错误长冷却,限流/超时短冷却(避免整渠道误杀,shenshi-gateway.md §4)。 const ( credentialCooldown = 10 * time.Minute bindingCooldown = 30 * time.Second ) // Cooldowns 进程内冷却状态:key 形如 "cred::" / "bind::"。 type Cooldowns struct { mu sync.Mutex until map[string]time.Time } // NewCooldowns 构造冷却表。 func NewCooldowns() *Cooldowns { return &Cooldowns{until: make(map[string]time.Time)} } // Mark 凭证级(credential=true)或绑定级冷却。 func (c *Cooldowns) Mark(key string, credential bool) { c.mu.Lock() defer c.mu.Unlock() duration := bindingCooldown if credential { duration = credentialCooldown } c.until[key] = time.Now().Add(duration) } // Active 该冷却键当前是否生效。 func (c *Cooldowns) Active(key string) bool { c.mu.Lock() defer c.mu.Unlock() t, ok := c.until[key] return ok && time.Now().Before(t) } // Sticky 粘性会话:同一 run 连续请求优先同端点(进程内,run 生命周期短)。 type Sticky struct { mu sync.Mutex binds map[string]string } // NewSticky 构造粘性表。 func NewSticky() *Sticky { return &Sticky{binds: make(map[string]string)} } // Remember 记录 run → 端点绑定。 func (s *Sticky) Remember(runID, endpointName string) { s.mu.Lock() defer s.mu.Unlock() s.binds[runID] = endpointName } // Recall 取 run 的粘性端点(无则空串)。 func (s *Sticky) Recall(runID string) string { s.mu.Lock() defer s.mu.Unlock() return s.binds[runID] } // Forget run 结束后清理。 func (s *Sticky) Forget(runID string) { s.mu.Lock() defer s.mu.Unlock() delete(s.binds, runID) }