feat: 网络接口负载采样 — 令牌环认领决策引入真实 NIC 饱和度

- netload_linux.go: 从 /proc/net/dev 字节增量 + /sys/class/net/<iface>/speed
  计算网卡利用率 (rx+tx 合计, 多网卡按容量加权, [0,100] 钳制)
- virtio VM speed=-1 时用 1Gbps 软容量兜底, 保持 VM 上采样有意义
- 排除 lo/docker*/br-*/veth*/tun/tap/wg/virbr* 等非物理 uplink,
  容器桥接流量不再虚增主机负载
- netload_other.go: 非 Linux 平台编译期 fallback 返回 0
- main.go LoadFn: NetPct 从写死的 10 换成 cluster.SampleNetLoad()
- Score = Forwards*100 + MemPct + NetPct: 主信号仍是转发数,
  net 在同转发数节点间打破平局 — NIC 已饱和的节点不再吸引新转发
- 单测: 采样范围断言 + 虚拟接口排除表

实测: 三台集群 net 列显示真实值 (0.02-0.03%), 不再是常量
This commit is contained in:
JianFeeeee
2026-08-24 16:48:12 +08:00
parent 88b862b515
commit 0596678c67
5 changed files with 302 additions and 7 deletions

View File

@ -135,7 +135,11 @@ func main() {
selfID, selfID, *user, *pass, "0.1.0", nil,
&cluster.AppHandler{
LoadFn: func() (float64, float64) {
return cluster.SampleMemLoad(), 10
// NetPct: real NIC saturation from /proc/net/dev deltas vs
// interface link speed (Linux); 0 fallback elsewhere. A NIC
// already near capacity should stop attracting new forwards —
// the forward itself is a network hop.
return cluster.SampleMemLoad(), cluster.SampleNetLoad()
},
// Claim persists the forward locally (intermediate config -> store),
// then syncs workers so the frpc worker for that remote starts.
@ -454,7 +458,10 @@ func main() {
log.Printf("ring[%s] reconcile: %v", ring.ID, err)
return
}
type ownerKey struct{ local, remote string; port int }
type ownerKey struct {
local, remote string
port int
}
ownedBy := map[ownerKey]string{}
for _, t := range snap.Topology {
k := ownerKey{t.Local.Name, t.Remote.Name, t.Link.RemotePort}