refactor: 审查修复 — netload 消除重复 /sys 读 + 全项目 gofmt

- netload_linux.go: SampleNetLoad 聚合循环不再对每接口重复
  readIfaceSpeed (snapshot 已汇总 cur.capMbps), 每次采样省 N 次 /sys 读
- gofmt -w: ring.go/ring_engine.go/auth.go/handlers.go/handlers_logs.go/
  handlers_users.go/store.go 结构体字段对齐与注释缩进
- README.md: markdownlint 自动修复 (MD028/MD040)

审查结论: 令牌环本身即互斥协议 — OnToken(收令牌)与 StartRing(发令牌)
在同一节点上由令牌串行化, 不存在需要加锁的竞争; WatchLeader 的读为
良性读, 无需 mutex
This commit is contained in:
JianFeeeee
2026-08-24 22:24:35 +08:00
parent 0596678c67
commit 4a41608d94
9 changed files with 45 additions and 37 deletions

View File

@ -196,8 +196,9 @@ func SampleNetLoad() float64 {
//
// rx+tx counts BOTH directions — a forward proxies in AND out, so a NIC
// that's saturated in one direction is saturated for forwarding purposes.
// Capacity comes from the CURRENT snapshot's capMbps (already summed in
// snapshotNet) — no extra /sys reads per interface here.
var deltaBits float64
sharedCap := uint64(0)
for name, c := range cur.bytes {
p, ok := prev.bytes[name]
if !ok {
@ -209,15 +210,11 @@ func SampleNetLoad() float64 {
continue
}
deltaBits += float64(c-p) * 8
// approximate this interface's capacity contribution proportional to
// its share of the summed speed — read back from /sys would be noisy
// per call, so reuse the prior snapshot's capMbps scaling.
sharedCap += readIfaceSpeed(name)
}
if sharedCap == 0 {
if cur.capMbps == 0 {
return 0
}
capBitsPerSec := float64(sharedCap) * 1e6
capBitsPerSec := float64(cur.capMbps) * 1e6
pct := (deltaBits / dt) / capBitsPerSec * 100
if pct < 0 {
return 0

View File

@ -81,9 +81,9 @@ type TopoEntry struct {
// State is the full cluster picture, replicated on every node:
// ring membership + pending tasks + active forward topology.
type State struct {
LeaderID string `json:"leaderId"`
Nodes []Node `json:"nodes"`
Cycle int64 `json:"cycle"`
LeaderID string `json:"leaderId"`
Nodes []Node `json:"nodes"`
Cycle int64 `json:"cycle"`
// RoundDelay rides the token so every node converges on the same ring
// cadence (serialized as nanoseconds; the leader refreshes it each round
// via tkDelaySince). MUST be serialized — otherwise a token round-trip

View File

@ -162,16 +162,17 @@ func (e *Engine) loadSnapshot() Load {
// OnToken is the SINGLE-ROUND token handler. Per the authoritative design
// (plan §令牌环协议): on receiving the token a node simultaneously
// (a) ADOPTS the carried cluster picture — incoming state is authoritative
// for membership + per-node fields. This is safe because structural
// changes (join/remove) are NOT kept in local state waiting to survive
// a merge: joins ride a separate pendingJoin channel injected INTO the
// token here, and a self-remove writes the node out of state so the
// downstream merge naturally drops it.
// (b) APPLIES the incremental log delta, then re-attaches own fresh entries.
// (c) APPENDS own node info (load/version) — skipped if we self-removed.
// (d) INJECTS pending newcomers right after ourselves + forwards to them.
// (e) Paces via a parallel rhythm timer (max(ops, timer)).
//
// (a) ADOPTS the carried cluster picture — incoming state is authoritative
// for membership + per-node fields. This is safe because structural
// changes (join/remove) are NOT kept in local state waiting to survive
// a merge: joins ride a separate pendingJoin channel injected INTO the
// token here, and a self-remove writes the node out of state so the
// downstream merge naturally drops it.
// (b) APPLIES the incremental log delta, then re-attaches own fresh entries.
// (c) APPENDS own node info (load/version) — skipped if we self-removed.
// (d) INJECTS pending newcomers right after ourselves + forwards to them.
// (e) Paces via a parallel rhythm timer (max(ops, timer)).
func (e *Engine) OnToken(ctx context.Context, tk *Token) (*Token, error) {
// Multi-token guard: only the newest leader-stamped token is kept.
if tk.SentAt > 0 && e.lastTokenAt > 0 && tk.SentAt < e.lastTokenAt {