183 lines
4.4 KiB
Go
183 lines
4.4 KiB
Go
|
|
package metrics
|
||
|
|
|
||
|
|
import (
|
||
|
|
"sync"
|
||
|
|
"sync/atomic"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// RoutingMetrics 路由指标收集器 (M-008)
|
||
|
|
type RoutingMetrics struct {
|
||
|
|
// 计数器
|
||
|
|
totalRequests int64
|
||
|
|
totalTakeovers int64
|
||
|
|
primaryTakeovers int64
|
||
|
|
fallbackTakeovers int64
|
||
|
|
noMarkCount int64
|
||
|
|
|
||
|
|
// 按provider统计
|
||
|
|
providerStats map[string]*ProviderStat
|
||
|
|
providerMu sync.RWMutex
|
||
|
|
|
||
|
|
// 按策略统计
|
||
|
|
strategyStats map[string]*StrategyStat
|
||
|
|
strategyMu sync.RWMutex
|
||
|
|
|
||
|
|
// 时间窗口
|
||
|
|
windowStart time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
// ProviderStat Provider统计
|
||
|
|
type ProviderStat struct {
|
||
|
|
Count int64
|
||
|
|
LatencySum int64
|
||
|
|
Errors int64
|
||
|
|
}
|
||
|
|
|
||
|
|
// StrategyStat 策略统计
|
||
|
|
type StrategyStat struct {
|
||
|
|
Count int64
|
||
|
|
Takeovers int64
|
||
|
|
LatencySum int64
|
||
|
|
}
|
||
|
|
|
||
|
|
// RoutingStats 路由统计
|
||
|
|
type RoutingStats struct {
|
||
|
|
TotalRequests int64
|
||
|
|
TotalTakeovers int64
|
||
|
|
PrimaryTakeovers int64
|
||
|
|
FallbackTakeovers int64
|
||
|
|
NoMarkCount int64
|
||
|
|
TakeoverRate float64
|
||
|
|
M008Coverage float64 // 路由标记覆盖率 >= 99.9%
|
||
|
|
ProviderStats map[string]*ProviderStat
|
||
|
|
StrategyStats map[string]*StrategyStat
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewRoutingMetrics 创建路由指标收集器
|
||
|
|
func NewRoutingMetrics() *RoutingMetrics {
|
||
|
|
return &RoutingMetrics{
|
||
|
|
providerStats: make(map[string]*ProviderStat),
|
||
|
|
strategyStats: make(map[string]*StrategyStat),
|
||
|
|
windowStart: time.Now(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// RecordTakeoverMark 记录接管标记
|
||
|
|
// pathType: "primary" 或 "fallback"
|
||
|
|
// strategy: 使用的策略名称
|
||
|
|
func (m *RoutingMetrics) RecordTakeoverMark(provider string, tier int, pathType string, strategy string) {
|
||
|
|
atomic.AddInt64(&m.totalTakeovers, 1)
|
||
|
|
|
||
|
|
// 更新路径类型计数
|
||
|
|
switch pathType {
|
||
|
|
case "primary":
|
||
|
|
atomic.AddInt64(&m.primaryTakeovers, 1)
|
||
|
|
case "fallback":
|
||
|
|
atomic.AddInt64(&m.fallbackTakeovers, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新Provider统计
|
||
|
|
m.providerMu.Lock()
|
||
|
|
if _, ok := m.providerStats[provider]; !ok {
|
||
|
|
m.providerStats[provider] = &ProviderStat{}
|
||
|
|
}
|
||
|
|
m.providerStats[provider].Count++
|
||
|
|
m.providerMu.Unlock()
|
||
|
|
|
||
|
|
// 更新策略统计
|
||
|
|
m.strategyMu.Lock()
|
||
|
|
if _, ok := m.strategyStats[strategy]; !ok {
|
||
|
|
m.strategyStats[strategy] = &StrategyStat{}
|
||
|
|
}
|
||
|
|
m.strategyStats[strategy].Count++
|
||
|
|
m.strategyStats[strategy].Takeovers++
|
||
|
|
m.strategyMu.Unlock()
|
||
|
|
}
|
||
|
|
|
||
|
|
// RecordNoMark 记录未标记的请求(用于计算覆盖率)
|
||
|
|
func (m *RoutingMetrics) RecordNoMark(reason string) {
|
||
|
|
atomic.AddInt64(&m.noMarkCount, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
// RecordRequest 记录请求
|
||
|
|
func (m *RoutingMetrics) RecordRequest() {
|
||
|
|
atomic.AddInt64(&m.totalRequests, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetStats 获取统计信息
|
||
|
|
func (m *RoutingMetrics) GetStats() *RoutingStats {
|
||
|
|
total := atomic.LoadInt64(&m.totalRequests)
|
||
|
|
takeovers := atomic.LoadInt64(&m.totalTakeovers)
|
||
|
|
primary := atomic.LoadInt64(&m.primaryTakeovers)
|
||
|
|
fallback := atomic.LoadInt64(&m.fallbackTakeovers)
|
||
|
|
noMark := atomic.LoadInt64(&m.noMarkCount)
|
||
|
|
|
||
|
|
// 计算接管率 (有标记的请求 / 总请求)
|
||
|
|
var takeoverRate float64
|
||
|
|
if total > 0 {
|
||
|
|
takeoverRate = float64(takeovers) / float64(total) * 100
|
||
|
|
}
|
||
|
|
|
||
|
|
// 计算M-008覆盖率 (有标记的请求 / 总请求)
|
||
|
|
var coverage float64
|
||
|
|
if total > 0 {
|
||
|
|
coverage = float64(takeovers) / float64(total) * 100
|
||
|
|
}
|
||
|
|
|
||
|
|
// 复制Provider统计
|
||
|
|
m.providerMu.RLock()
|
||
|
|
providerStats := make(map[string]*ProviderStat)
|
||
|
|
for k, v := range m.providerStats {
|
||
|
|
providerStats[k] = &ProviderStat{
|
||
|
|
Count: v.Count,
|
||
|
|
LatencySum: v.LatencySum,
|
||
|
|
Errors: v.Errors,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
m.providerMu.RUnlock()
|
||
|
|
|
||
|
|
// 复制策略统计
|
||
|
|
m.strategyMu.RLock()
|
||
|
|
strategyStats := make(map[string]*StrategyStat)
|
||
|
|
for k, v := range m.strategyStats {
|
||
|
|
strategyStats[k] = &StrategyStat{
|
||
|
|
Count: v.Count,
|
||
|
|
Takeovers: v.Takeovers,
|
||
|
|
LatencySum: v.LatencySum,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
m.strategyMu.RUnlock()
|
||
|
|
|
||
|
|
return &RoutingStats{
|
||
|
|
TotalRequests: total,
|
||
|
|
TotalTakeovers: takeovers,
|
||
|
|
PrimaryTakeovers: primary,
|
||
|
|
FallbackTakeovers: fallback,
|
||
|
|
NoMarkCount: noMark,
|
||
|
|
TakeoverRate: takeoverRate,
|
||
|
|
M008Coverage: coverage,
|
||
|
|
ProviderStats: providerStats,
|
||
|
|
StrategyStats: strategyStats,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reset 重置统计
|
||
|
|
func (m *RoutingMetrics) Reset() {
|
||
|
|
atomic.StoreInt64(&m.totalRequests, 0)
|
||
|
|
atomic.StoreInt64(&m.totalTakeovers, 0)
|
||
|
|
atomic.StoreInt64(&m.primaryTakeovers, 0)
|
||
|
|
atomic.StoreInt64(&m.fallbackTakeovers, 0)
|
||
|
|
atomic.StoreInt64(&m.noMarkCount, 0)
|
||
|
|
|
||
|
|
m.providerMu.Lock()
|
||
|
|
m.providerStats = make(map[string]*ProviderStat)
|
||
|
|
m.providerMu.Unlock()
|
||
|
|
|
||
|
|
m.strategyMu.Lock()
|
||
|
|
m.strategyStats = make(map[string]*StrategyStat)
|
||
|
|
m.strategyMu.Unlock()
|
||
|
|
|
||
|
|
m.windowStart = time.Now()
|
||
|
|
}
|