// Package traffic 统计经过本进程的 HTTP 流量(请求体 + 响应体),供后台评估带宽。 // 说明:前有 Nginx 时边缘出口可能更大;WebSocket 升级后部分流量可能不经此计数。 package traffic import ( "sync" "sync/atomic" "time" ) var ( totalIn atomic.Uint64 totalOut atomic.Uint64 started = time.Now() tickerOnce sync.Once mu sync.Mutex secIn [60]uint64 secOut [60]uint64 lastSnapIn uint64 lastSnapOut uint64 tickIndex int64 ) func ensureTicker() { tickerOnce.Do(func() { go func() { t := time.NewTicker(time.Second) defer t.Stop() for range t.C { tick() } }() }) } func tick() { ti := totalIn.Load() to := totalOut.Load() mu.Lock() i := int(tickIndex % 60) secIn[i] = ti - lastSnapIn secOut[i] = to - lastSnapOut lastSnapIn = ti lastSnapOut = to tickIndex++ mu.Unlock() } // AddIn 记录请求体已读字节。 func AddIn(n int) { if n > 0 { ensureTicker() totalIn.Add(uint64(n)) } } // AddOut 记录响应已写字节。 func AddOut(n int) { if n > 0 { ensureTicker() totalOut.Add(uint64(n)) } } // Snapshot 返回当前统计(近 60 秒为滚动窗口内各秒增量之和)。 func Snapshot() map[string]any { ensureTicker() tin := totalIn.Load() tout := totalOut.Load() up := time.Since(started).Seconds() mu.Lock() var sumIn, sumOut uint64 for i := 0; i < 60; i++ { sumIn += secIn[i] sumOut += secOut[i] } mu.Unlock() var avgDown, avgUp, recentDown, recentUp float64 if up > 0.5 { avgDown = float64(tout) * 8 / (up * 1e6) // Mbps 出站(自启动平均) avgUp = float64(tin) * 8 / (up * 1e6) // Mbps 入站 } recentDown = float64(sumOut) * 8 / (60 * 1e6) recentUp = float64(sumIn) * 8 / (60 * 1e6) return map[string]any{ "bytes_in_total": tin, "bytes_out_total": tout, "bytes_in_last_60s": sumIn, "bytes_out_last_60s": sumOut, "uptime_seconds": up, "avg_egress_mbps": round2(avgDown), "avg_ingress_mbps": round2(avgUp), "recent_egress_mbps": round2(recentDown), "recent_ingress_mbps": round2(recentUp), } } func round2(x float64) float64 { if x < 0 { return 0 } return float64(int64(x*100+0.5)) / 100 }