Yury Kastov
2026-03-07 13:56:11 +03:00
committed by GitHub
parent 78fc2865ea
commit eec280262d
5 changed files with 88 additions and 77 deletions

View File

@@ -183,12 +183,9 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
if p.Stats.UserOnline { if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online" name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil { if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx) userIP := sessionInbound.Source.Address.String()
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP) om.AddIP(userIP)
// log Online user with ips context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
} }
} }
} }
@@ -225,11 +222,9 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
if p.Stats.UserOnline { if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online" name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(statsManager, name); om != nil { if om, _ := stats.GetOrRegisterOnlineMap(statsManager, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx) userIP := sessionInbound.Source.Address.String()
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP) om.AddIP(userIP)
// log Online user with ips context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
} }
} }
} }

View File

@@ -70,7 +70,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
} }
ips := make(map[string]int64) ips := make(map[string]int64)
for ip, t := range c.IpTimeMap() { for ip, t := range c.IPTimeMap() {
ips[ip] = t.Unix() ips[ip] = t.Unix()
} }

View File

@@ -2,84 +2,98 @@ package stats
import ( import (
"sync" "sync"
"sync/atomic"
"time" "time"
) )
// OnlineMap is an implementation of stats.OnlineMap. const (
type OnlineMap struct { localhostIPv4 = "127.0.0.1"
ipList map[string]time.Time localhostIPv6 = "[::1]"
access sync.RWMutex )
lastCleanup time.Time
cleanupPeriod time.Duration type ipEntry struct {
refCount int
lastSeen time.Time
} }
// NewOnlineMap creates a new instance of OnlineMap. // OnlineMap is a refcount-based implementation of stats.OnlineMap.
// IPs are tracked by reference counting: AddIP increments, RemoveIP decrements.
// An IP is removed from the map when its reference count reaches zero.
type OnlineMap struct {
entries map[string]*ipEntry
access sync.Mutex
count atomic.Int64
}
// NewOnlineMap creates a new OnlineMap instance.
func NewOnlineMap() *OnlineMap { func NewOnlineMap() *OnlineMap {
return &OnlineMap{ return &OnlineMap{
ipList: make(map[string]time.Time), entries: make(map[string]*ipEntry),
lastCleanup: time.Now(), }
cleanupPeriod: 10 * time.Second, }
// AddIP implements stats.OnlineMap.
func (om *OnlineMap) AddIP(ip string) {
if ip == localhostIPv4 || ip == localhostIPv6 {
return
}
om.access.Lock()
defer om.access.Unlock()
if e, ok := om.entries[ip]; ok {
e.refCount++
e.lastSeen = time.Now()
} else {
om.entries[ip] = &ipEntry{
refCount: 1,
lastSeen: time.Now(),
}
om.count.Add(1)
}
}
// RemoveIP implements stats.OnlineMap.
func (om *OnlineMap) RemoveIP(ip string) {
om.access.Lock()
defer om.access.Unlock()
e, ok := om.entries[ip]
if !ok {
return
}
e.refCount--
if e.refCount <= 0 {
delete(om.entries, ip)
om.count.Add(-1)
} }
} }
// Count implements stats.OnlineMap. // Count implements stats.OnlineMap.
func (c *OnlineMap) Count() int { func (om *OnlineMap) Count() int {
c.access.RLock() return int(om.count.Load())
defer c.access.RUnlock()
return len(c.ipList)
} }
// List implements stats.OnlineMap. // List implements stats.OnlineMap.
func (c *OnlineMap) List() []string { func (om *OnlineMap) List() []string {
return c.GetKeys() om.access.Lock()
} defer om.access.Unlock()
// AddIP implements stats.OnlineMap. keys := make([]string, 0, len(om.entries))
func (c *OnlineMap) AddIP(ip string) { for ip := range om.entries {
if ip == "127.0.0.1" { keys = append(keys, ip)
return
}
c.access.Lock()
c.ipList[ip] = time.Now()
c.access.Unlock()
if time.Since(c.lastCleanup) > c.cleanupPeriod {
c.RemoveExpiredIPs()
c.lastCleanup = time.Now()
}
}
func (c *OnlineMap) GetKeys() []string {
c.access.RLock()
defer c.access.RUnlock()
keys := []string{}
for k := range c.ipList {
keys = append(keys, k)
} }
return keys return keys
} }
func (c *OnlineMap) RemoveExpiredIPs() { // IPTimeMap implements stats.OnlineMap.
c.access.Lock() func (om *OnlineMap) IPTimeMap() map[string]time.Time {
defer c.access.Unlock() om.access.Lock()
defer om.access.Unlock()
now := time.Now() result := make(map[string]time.Time, len(om.entries))
for k, t := range c.ipList { for ip, e := range om.entries {
diff := now.Sub(t) result[ip] = e.lastSeen
if diff.Seconds() > 20 {
delete(c.ipList, k)
}
} }
} return result
func (c *OnlineMap) IpTimeMap() map[string]time.Time {
if time.Since(c.lastCleanup) > c.cleanupPeriod {
c.RemoveExpiredIPs()
c.lastCleanup = time.Now()
}
return c.ipList
} }

View File

@@ -163,12 +163,12 @@ func (m *Manager) GetChannel(name string) stats.Channel {
// GetAllOnlineUsers implements stats.Manager. // GetAllOnlineUsers implements stats.Manager.
func (m *Manager) GetAllOnlineUsers() []string { func (m *Manager) GetAllOnlineUsers() []string {
m.access.Lock() m.access.RLock()
defer m.access.Unlock() defer m.access.RUnlock()
usersOnline := make([]string, 0, len(m.onlineMap)) usersOnline := make([]string, 0, len(m.onlineMap))
for user, onlineMap := range m.onlineMap { for user, onlineMap := range m.onlineMap {
if len(onlineMap.IpTimeMap()) > 0 { if onlineMap.Count() > 0 {
usersOnline = append(usersOnline, user) usersOnline = append(usersOnline, user)
} }
} }

View File

@@ -25,14 +25,16 @@ type Counter interface {
// //
// xray:api:stable // xray:api:stable
type OnlineMap interface { type OnlineMap interface {
// Count is the current value of the OnlineMap. // Count returns the number of unique online IPs.
Count() int Count() int
// AddIP adds a ip to the current OnlineMap. // AddIP increments the reference count for the given IP.
AddIP(string) AddIP(string)
// List is the current OnlineMap ip list. // RemoveIP decrements the reference count for the given IP. Deletes at zero.
RemoveIP(string)
// List returns all currently online IPs.
List() []string List() []string
// IpTimeMap return client ips and their last access time. // IPTimeMap returns a snapshot copy of IPs to their last-seen times.
IpTimeMap() map[string]time.Time IPTimeMap() map[string]time.Time
} }
// Channel is the interface for stats channel. // Channel is the interface for stats channel.