Files
SeeyonFileSystem/server/middleware/traffic.go

168 lines
4.2 KiB
Go
Raw Normal View History

2026-07-03 16:01:08 +08:00
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
}