feat: multi-node device limit, optimize rate limiter

This commit is contained in:
xboard
2026-03-25 17:38:40 +08:00
parent 7e3ad7cb23
commit b51cecfe17
11 changed files with 616 additions and 151 deletions
+51 -1
View File
@@ -1,6 +1,9 @@
package tracker
import (
"crypto/sha256"
"encoding/hex"
"sort"
"sync"
"sync/atomic"
@@ -49,6 +52,9 @@ type Tracker struct {
// aliveIPsBuf is a reusable buffer for FlushAliveIPs output.
// Avoids allocating a new map+slice every 60s.
aliveIPsBuf map[int][]string
// lastAliveIPsHash detects changes to avoid duplicate reports.
lastAliveIPsHash string
}
func New() *Tracker {
@@ -154,13 +160,23 @@ func (t *Tracker) HasTraffic() bool {
}
// FlushAliveIPs returns per-user alive IPs.
// Reuses internal buffer to avoid allocation every 60s.
// Reuses internal buffer. Returns nil if unchanged.
func (t *Tracker) FlushAliveIPs() map[int][]string {
s := t.live.Load()
t.mu.Lock()
defer t.mu.Unlock()
// Calculate hash of current aliveIPs
currentHash := calcAliveIPsHash(s.aliveIPs)
// If no changes, return nil to avoid duplicate reporting
if currentHash == t.lastAliveIPsHash {
return nil
}
t.lastAliveIPsHash = currentHash
// Clear old buffer entries.
for k := range t.aliveIPsBuf {
delete(t.aliveIPsBuf, k)
@@ -182,6 +198,40 @@ func (t *Tracker) FlushAliveIPs() map[int][]string {
return t.aliveIPsBuf
}
// calcAliveIPsHash computes a deterministic hash for change detection.
func calcAliveIPsHash(aliveIPs map[int]map[string]bool) string {
if len(aliveIPs) == 0 {
return ""
}
h := sha256.New()
// Sort user IDs for consistent hashing
userIDs := make([]int, 0, len(aliveIPs))
for uid := range aliveIPs {
userIDs = append(userIDs, uid)
}
sort.Ints(userIDs)
for _, uid := range userIDs {
ips := aliveIPs[uid]
// Sort IPs for consistent hashing
ipList := make([]string, 0, len(ips))
for ip := range ips {
ipList = append(ipList, ip)
}
sort.Strings(ipList)
// Write user ID and IPs to hash
h.Write([]byte{byte(uid >> 24), byte(uid >> 16), byte(uid >> 8), byte(uid)})
for _, ip := range ipList {
h.Write([]byte(ip))
}
}
return hex.EncodeToString(h.Sum(nil))
}
// CurrentOnline returns a snapshot copy of user_id → device count (distinct IPs).
// Lock-free: reads from live snapshot.
func (t *Tracker) CurrentOnline() map[int]int {