Files
web/server/pkg/traffic/meter.go

109 lines
2.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}