76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
// 候选路由与冷却:凭证级(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:<endpoint>:<keyhash>" / "bind:<endpoint>:<model>"。
|
|
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)
|
|
}
|