Files
lijiaoqiao/gateway/internal/router/fallback/fallback.go
Your Name 89104bd0db feat(P1/P2): 完成TDD开发及P1/P2设计文档
## 设计文档
- multi_role_permission_design: 多角色权限设计 (CONDITIONAL GO)
- audit_log_enhancement_design: 审计日志增强 (CONDITIONAL GO)
- routing_strategy_template_design: 路由策略模板 (CONDITIONAL GO)
- sso_saml_technical_research: SSO/SAML调研 (CONDITIONAL GO)
- compliance_capability_package_design: 合规能力包设计 (CONDITIONAL GO)

## TDD开发成果
- IAM模块: supply-api/internal/iam/ (111个测试)
- 审计日志模块: supply-api/internal/audit/ (40+测试)
- 路由策略模块: gateway/internal/router/ (33+测试)
- 合规能力包: gateway/internal/compliance/ + scripts/ci/compliance/

## 规范文档
- parallel_agent_output_quality_standards: 并行Agent产出质量规范
- project_experience_summary: 项目经验总结 (v2)
- 2026-04-02-p1-p2-tdd-execution-plan: TDD执行计划

## 评审报告
- 5个CONDITIONAL GO设计文档评审报告
- fix_verification_report: 修复验证报告
- full_verification_report: 全面质量验证报告
- tdd_module_quality_verification: TDD模块质量验证
- tdd_execution_summary: TDD执行总结

依据: Superpowers执行框架 + TDD规范
2026-04-02 23:35:53 +08:00

146 lines
3.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package fallback
import (
"context"
"errors"
"lijiaoqiao/gateway/internal/adapter"
"lijiaoqiao/gateway/internal/router/strategy"
)
// ErrAllTiersFailed 所有Fallback层级都失败
var ErrAllTiersFailed = errors.New("all fallback tiers failed")
// ErrRateLimitExceeded 限流错误
var ErrRateLimitExceeded = errors.New("rate limit exceeded")
// FallbackHandler Fallback处理器
type FallbackHandler struct {
tiers []TierConfig
router FallbackRouter
metrics FallbackMetrics
providerGetter ProviderGetter
}
// TierConfig Fallback层级配置
type TierConfig struct {
Tier int
Providers []string
TimeoutMs int64
}
// FallbackMetrics Fallback指标接口
type FallbackMetrics interface {
RecordTakeoverMark(provider string, tier int)
}
// ProviderGetter Provider获取器接口
type ProviderGetter interface {
GetProvider(name string) adapter.ProviderAdapter
}
// FallbackRouter Fallback路由器接口
type FallbackRouter interface {
SelectProvider(ctx context.Context, req *strategy.RoutingRequest, providerName string) (*strategy.RoutingDecision, error)
}
// NewFallbackHandler 创建Fallback处理器
func NewFallbackHandler() *FallbackHandler {
return &FallbackHandler{
tiers: make([]TierConfig, 0),
}
}
// SetTiers 设置Fallback层级
func (h *FallbackHandler) SetTiers(tiers []TierConfig) {
h.tiers = tiers
}
// SetRouter 设置路由器
func (h *FallbackHandler) SetRouter(router FallbackRouter) {
h.router = router
}
// SetMetrics 设置指标收集器
func (h *FallbackHandler) SetMetrics(metrics FallbackMetrics) {
h.metrics = metrics
}
// SetProviderGetter 设置Provider获取器
func (h *FallbackHandler) SetProviderGetter(getter ProviderGetter) {
h.providerGetter = getter
}
// Handle 处理Fallback
func (h *FallbackHandler) Handle(ctx context.Context, req *strategy.RoutingRequest) (*strategy.RoutingDecision, error) {
if len(h.tiers) == 0 {
return nil, ErrAllTiersFailed
}
// 按层级顺序尝试
for _, tier := range h.tiers {
decision, err := h.tryTier(ctx, req, tier)
if err == nil {
// 成功,记录指标
if h.metrics != nil {
h.metrics.RecordTakeoverMark(decision.Provider, tier.Tier)
}
return decision, nil
}
// 检查是否是限流错误
if errors.Is(err, ErrRateLimitExceeded) {
// 限流错误立即返回,不继续降级
return nil, err
}
// 其他错误,尝试下一层级
continue
}
return nil, ErrAllTiersFailed
}
// tryTier 尝试单个层级
func (h *FallbackHandler) tryTier(ctx context.Context, req *strategy.RoutingRequest, tier TierConfig) (*strategy.RoutingDecision, error) {
for _, providerName := range tier.Providers {
decision, err := h.router.SelectProvider(ctx, req, providerName)
if err == nil {
decision.TakeoverMark = true
return decision, nil
}
// 检查是否是限流错误
if isRateLimitError(err) {
return nil, ErrRateLimitExceeded
}
// 其他错误继续尝试下一个provider
continue
}
return nil, ErrAllTiersFailed
}
// isRateLimitError 判断是否是限流错误
func isRateLimitError(err error) bool {
if err == nil {
return false
}
// 检查错误消息中是否包含rate limit
return containsRateLimit(err.Error())
}
func containsRateLimit(s string) bool {
return len(s) > 0 && (contains(s, "rate limit") || contains(s, "ratelimit") || contains(s, "too many requests"))
}
func contains(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}