109 lines
2.2 KiB
Go
109 lines
2.2 KiB
Go
// 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
|
||
}
|