refactor(analytic): improved network stat #913

This commit is contained in:
Jacky 2025-03-15 09:09:53 +08:00
parent ec29a77f99
commit 789698a0d4
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
6 changed files with 247 additions and 108 deletions

View file

@ -1,9 +1,9 @@
package analytic
import (
"github.com/shirou/gopsutil/v4/net"
"github.com/uozi-tech/cosy/logger"
"time"
"github.com/uozi-tech/cosy/logger"
)
type Usage[T uint64 | float64] struct {
@ -25,14 +25,12 @@ var (
)
func init() {
network, err := net.IOCounters(false)
network, err := GetNetworkStat()
if err != nil {
logger.Error(err)
}
if len(network) > 0 {
LastNetRecv = network[0].BytesRecv
LastNetSent = network[0].BytesSent
}
LastNetRecv = network.BytesRecv
LastNetSent = network.BytesSent
LastDiskReads, LastDiskWrites = getTotalDiskIO()

View file

@ -0,0 +1,220 @@
package analytic
import (
stdnet "net"
"github.com/shirou/gopsutil/v4/net"
"github.com/uozi-tech/cosy/logger"
)
func GetNetworkStat() (data *net.IOCountersStat, err error) {
networkStats, err := net.IOCounters(true)
if err != nil {
return
}
if len(networkStats) == 0 {
return &net.IOCountersStat{}, nil
}
// Get all network interfaces
interfaces, err := stdnet.Interfaces()
if err != nil {
logger.Error(err)
return
}
var (
totalBytesRecv uint64
totalBytesSent uint64
totalPacketsRecv uint64
totalPacketsSent uint64
totalErrIn uint64
totalErrOut uint64
totalDropIn uint64
totalDropOut uint64
totalFifoIn uint64
totalFifoOut uint64
)
// Create a map of external interface names
externalInterfaces := make(map[string]bool)
// Identify external interfaces
for _, iface := range interfaces {
// Skip down or loopback interfaces
if iface.Flags&stdnet.FlagUp == 0 ||
iface.Flags&stdnet.FlagLoopback != 0 {
continue
}
// Get addresses for this interface
addrs, err := iface.Addrs()
if err != nil {
logger.Error(err)
continue
}
// Skip interfaces without addresses
if len(addrs) == 0 {
continue
}
// Check for non-private IP addresses
for _, addr := range addrs {
ip, ipNet, err := stdnet.ParseCIDR(addr.String())
if err != nil {
continue
}
// Skip virtual, local, multicast, and special purpose IPs
if !isRealExternalIP(ip, ipNet) {
continue
}
externalInterfaces[iface.Name] = true
break
}
}
// Accumulate stats only from external interfaces
for _, stat := range networkStats {
if externalInterfaces[stat.Name] {
totalBytesRecv += stat.BytesRecv
totalBytesSent += stat.BytesSent
totalPacketsRecv += stat.PacketsRecv
totalPacketsSent += stat.PacketsSent
totalErrIn += stat.Errin
totalErrOut += stat.Errout
totalDropIn += stat.Dropin
totalDropOut += stat.Dropout
totalFifoIn += stat.Fifoin
totalFifoOut += stat.Fifoout
}
}
return &net.IOCountersStat{
Name: "analytic.network",
BytesRecv: totalBytesRecv,
BytesSent: totalBytesSent,
PacketsRecv: totalPacketsRecv,
PacketsSent: totalPacketsSent,
Errin: totalErrIn,
Errout: totalErrOut,
Dropin: totalDropIn,
Dropout: totalDropOut,
Fifoin: totalFifoIn,
Fifoout: totalFifoOut,
}, nil
}
// isRealExternalIP checks if an IP is a genuine external (public) IP
func isRealExternalIP(ip stdnet.IP, ipNet *stdnet.IPNet) bool {
// Skip if it's not a global unicast address
if !ip.IsGlobalUnicast() {
return false
}
// Skip private IPs
if ip.IsPrivate() {
return false
}
// Skip link-local addresses
if ip.IsLinkLocalUnicast() {
return false
}
// Skip loopback
if ip.IsLoopback() {
return false
}
// Skip multicast
if ip.IsMulticast() {
return false
}
// Check for special reserved ranges
if isReservedIP(ip) {
return false
}
return true
}
// isReservedIP checks if an IP belongs to special reserved ranges
func isReservedIP(ip stdnet.IP) bool {
// Handle IPv4
if ip4 := ip.To4(); ip4 != nil {
// TEST-NET-1: 192.0.2.0/24 (RFC 5737)
if ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2 {
return true
}
// TEST-NET-2: 198.51.100.0/24 (RFC 5737)
if ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100 {
return true
}
// TEST-NET-3: 203.0.113.0/24 (RFC 5737)
if ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113 {
return true
}
// Benchmark tests: 198.18.0.0/15 (includes 198.19.0.0/16) (RFC 2544)
if ip4[0] == 198 && (ip4[1] == 18 || ip4[1] == 19) {
return true
}
// Documentation: 240.0.0.0/4 (RFC 1112)
if ip4[0] >= 240 {
return true
}
// CGNAT: 100.64.0.0/10 (RFC 6598)
if ip4[0] == 100 && (ip4[1]&0xC0) == 64 {
return true
}
} else if ip.To16() != nil {
// Documentation prefix (2001:db8::/32) - RFC 3849
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x0d && ip[3] == 0xb8 {
return true
}
// Unique Local Addresses (fc00::/7) - RFC 4193
if (ip[0] & 0xfe) == 0xfc {
return true
}
// 6to4 relay (2002::/16) - RFC 3056
if ip[0] == 0x20 && ip[1] == 0x02 {
return true
}
// Teredo tunneling (2001:0::/32) - RFC 4380
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && ip[3] == 0x00 {
return true
}
// Deprecated site-local addresses (fec0::/10) - RFC 3879
if (ip[0]&0xff) == 0xfe && (ip[1]&0xc0) == 0xc0 {
return true
}
// Old 6bone addresses (3ffe::/16) - Deprecated
if ip[0] == 0x3f && ip[1] == 0xfe {
return true
}
// ORCHID addresses (2001:10::/28) - RFC 4843
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x10 {
return true
}
// ORCHID v2 addresses (2001:20::/28) - RFC 7343
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x20 {
return true
}
}
return false
}

View file

@ -1,13 +1,13 @@
package analytic
import (
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/net"
"github.com/uozi-tech/cosy/logger"
"math"
"runtime"
"time"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/load"
"github.com/uozi-tech/cosy/logger"
)
func GetNodeStat() (data NodeStat) {
@ -37,22 +37,17 @@ func GetNodeStat() (data NodeStat) {
return
}
netIO, err := net.IOCounters(false)
network, err := GetNetworkStat()
if err != nil {
logger.Error(err)
return
}
var network net.IOCountersStat
if len(netIO) > 0 {
network = netIO[0]
}
return NodeStat{
AvgLoad: loadAvg,
CPUPercent: math.Min((cpuUserUsage+cpuSystemUsage)*100, 100),
MemoryPercent: memory.Pressure,
DiskPercent: diskStat.Percentage,
Network: network,
Network: *network,
}
}

View file

@ -1,13 +1,11 @@
package analytic
import (
stdnet "net"
"runtime"
"time"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/net"
"github.com/uozi-tech/cosy/logger"
)
@ -71,87 +69,22 @@ func recordCpu(now time.Time) {
func recordNetwork(now time.Time) {
// Get separate statistics for each interface
networkStats, err := net.IOCounters(true)
networkStats, err := GetNetworkStat()
if err != nil {
logger.Error(err)
return
}
if len(networkStats) == 0 {
return
}
// Get all network interfaces
interfaces, err := stdnet.Interfaces()
if err != nil {
logger.Error(err)
return
}
var totalBytesRecv uint64
var totalBytesSent uint64
var externalInterfaceFound bool
// Iterate through all interfaces to find external ones
for _, iface := range interfaces {
// Skip interfaces that are down
if iface.Flags&stdnet.FlagUp == 0 {
continue
}
// Get IP addresses for the interface
addrs, err := iface.Addrs()
if err != nil {
logger.Error(err)
continue
}
// Check if this is an external interface
for _, addr := range addrs {
if ipNet, ok := addr.(*stdnet.IPNet); ok {
// Exclude loopback addresses and private IPs
if !ipNet.IP.IsLoopback() {
// Found external interface, accumulate its statistics
for _, stat := range networkStats {
if stat.Name == iface.Name {
totalBytesRecv += stat.BytesRecv
totalBytesSent += stat.BytesSent
externalInterfaceFound = true
break
}
}
break
}
}
}
}
// If no external interface is found, use fallback option
if !externalInterfaceFound {
// Fallback: use all non-loopback interfaces
for _, iface := range interfaces {
if iface.Flags&stdnet.FlagLoopback == 0 && iface.Flags&stdnet.FlagUp != 0 {
for _, stat := range networkStats {
if stat.Name == iface.Name {
totalBytesRecv += stat.BytesRecv
totalBytesSent += stat.BytesSent
break
}
}
}
}
}
LastNetRecv = totalBytesRecv
LastNetSent = totalBytesSent
LastNetRecv = networkStats.BytesRecv
LastNetSent = networkStats.BytesSent
NetRecvRecord = append(NetRecvRecord, Usage[uint64]{
Time: now,
Usage: totalBytesRecv - LastNetRecv,
Usage: networkStats.BytesRecv - LastNetRecv,
})
NetSentRecord = append(NetSentRecord, Usage[uint64]{
Time: now,
Usage: totalBytesSent - LastNetSent,
Usage: networkStats.BytesSent - LastNetSent,
})
if len(NetRecvRecord) > 100 {