Files
hunjing/shenshi/internal/route/failover.go
T

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)
}