package middleware import ( "log" "sync" "sync/atomic" "time" "github.com/gin-gonic/gin" ) // TrafficStats 全局流量统计 var TrafficStats = &trafficStats{ startTime: time.Now(), } type trafficStats struct { startTime time.Time totalRequests int64 totalBytesIn int64 totalBytesOut int64 activeConns int64 // 按状态码统计 status2xx int64 status3xx int64 status4xx int64 status5xx int64 // 最近请求记录(滑动窗口) recentRequests []requestRecord mu sync.Mutex } type requestRecord struct { Timestamp time.Time IP string Path string Status int Bytes int } // TrafficInfo 流量信息 type TrafficInfo struct { Uptime string `json:"uptime"` TotalRequests int64 `json:"total_requests"` TotalBytesIn int64 `json:"total_bytes_in"` TotalBytesOut int64 `json:"total_bytes_out"` ActiveConns int64 `json:"active_conns"` QPS float64 `json:"qps"` // 每秒请求数 Status2xx int64 `json:"status_2xx"` Status3xx int64 `json:"status_3xx"` Status4xx int64 `json:"status_4xx"` Status5xx int64 `json:"status_5xx"` } // GetTrafficInfo 获取流量统计信息 func GetTrafficInfo() TrafficInfo { uptime := time.Since(TrafficStats.startTime).Truncate(time.Second) // 计算QPS(最近60秒) TrafficStats.mu.Lock() cutoff := time.Now().Add(-60 * time.Second) recent := 0 for _, r := range TrafficStats.recentRequests { if r.Timestamp.After(cutoff) { recent++ } } TrafficStats.mu.Unlock() var qps float64 if uptime.Seconds() > 0 { qps = float64(recent) / 60.0 } return TrafficInfo{ Uptime: uptime.String(), TotalRequests: atomic.LoadInt64(&TrafficStats.totalRequests), TotalBytesIn: atomic.LoadInt64(&TrafficStats.totalBytesIn), TotalBytesOut: atomic.LoadInt64(&TrafficStats.totalBytesOut), ActiveConns: atomic.LoadInt64(&TrafficStats.activeConns), QPS: qps, Status2xx: atomic.LoadInt64(&TrafficStats.status2xx), Status3xx: atomic.LoadInt64(&TrafficStats.status3xx), Status4xx: atomic.LoadInt64(&TrafficStats.status4xx), Status5xx: atomic.LoadInt64(&TrafficStats.status5xx), } } // TrafficMonitor 流量监控中间件 func TrafficMonitor() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() // 增加活跃连接数 atomic.AddInt64(&TrafficStats.activeConns, 1) defer atomic.AddInt64(&TrafficStats.activeConns, -1) // 统计请求大小 bytesIn := int64(c.Request.ContentLength) if bytesIn < 0 { bytesIn = 0 } atomic.AddInt64(&TrafficStats.totalBytesIn, bytesIn) atomic.AddInt64(&TrafficStats.totalRequests, 1) c.Next() // 统计响应 status := c.Writer.Status() bytesOut := int64(c.Writer.Size()) switch { case status >= 200 && status < 300: atomic.AddInt64(&TrafficStats.status2xx, 1) case status >= 300 && status < 400: atomic.AddInt64(&TrafficStats.status3xx, 1) case status >= 400 && status < 500: atomic.AddInt64(&TrafficStats.status4xx, 1) case status >= 500: atomic.AddInt64(&TrafficStats.status5xx, 1) } // 记录最近请求 TrafficStats.mu.Lock() TrafficStats.recentRequests = append(TrafficStats.recentRequests, requestRecord{ Timestamp: start, IP: c.ClientIP(), Path: c.Request.URL.Path, Status: status, Bytes: int(bytesOut), }) // 只保留最近5分钟的记录 cutoff := time.Now().Add(-5 * time.Minute) valid := TrafficStats.recentRequests[:0] for _, r := range TrafficStats.recentRequests { if r.Timestamp.After(cutoff) { valid = append(valid, r) } } TrafficStats.recentRequests = valid TrafficStats.mu.Unlock() // 慢请求日志 duration := time.Since(start) if duration > 5*time.Second { log.Printf("[SLOW] %s %s %d %v", c.ClientIP(), c.Request.URL.Path, status, duration) } } } // GetRecentRequests 获取最近的请求记录 func GetRecentRequests(limit int) []requestRecord { TrafficStats.mu.Lock() defer TrafficStats.mu.Unlock() total := len(TrafficStats.recentRequests) if limit <= 0 || limit > total { limit = total } start := total - limit result := make([]requestRecord, limit) copy(result, TrafficStats.recentRequests[start:]) return result }