Files
sing-box-extended-mirror/experimental/clashapi/trafficontrol/manager.go
T

182 lines
4.1 KiB
Go
Raw Normal View History

2022-07-22 09:29:13 +08:00
package trafficontrol
2022-07-19 22:16:49 +08:00
import (
2023-04-11 16:43:45 +08:00
"runtime"
2024-06-11 21:16:33 +08:00
"sync"
2025-08-25 19:49:12 +08:00
"sync/atomic"
2022-07-19 22:16:49 +08:00
"time"
2025-09-05 15:49:06 +08:00
"github.com/sagernet/sing-box/common/compatible"
2024-06-11 21:16:33 +08:00
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/observable"
2024-06-11 21:16:33 +08:00
"github.com/sagernet/sing/common/x/list"
"github.com/gofrs/uuid/v5"
2022-07-19 22:16:49 +08:00
)
type ConnectionEventType int
const (
ConnectionEventNew ConnectionEventType = iota
ConnectionEventUpdate
ConnectionEventClosed
)
type ConnectionEvent struct {
Type ConnectionEventType
ID uuid.UUID
Metadata *TrackerMetadata
UplinkDelta int64
DownlinkDelta int64
ClosedAt time.Time
}
2026-01-17 19:15:40 +08:00
const closedConnectionsLimit = 1000
2022-07-19 22:16:49 +08:00
type Manager struct {
2023-04-14 20:55:05 +08:00
uploadTotal atomic.Int64
downloadTotal atomic.Int64
2024-06-11 21:16:33 +08:00
connections compatible.Map[uuid.UUID, Tracker]
closedConnectionsAccess sync.Mutex
2026-01-17 19:15:40 +08:00
closedConnections list.List[TrackerMetadata]
memory uint64
eventSubscriber *observable.Subscriber[ConnectionEvent]
2022-07-19 22:16:49 +08:00
}
func NewManager() *Manager {
2024-11-27 18:05:30 +08:00
return &Manager{}
2022-07-19 22:16:49 +08:00
}
func (m *Manager) SetEventHook(subscriber *observable.Subscriber[ConnectionEvent]) {
m.eventSubscriber = subscriber
}
2024-06-11 21:16:33 +08:00
func (m *Manager) Join(c Tracker) {
metadata := c.Metadata()
m.connections.Store(metadata.ID, c)
if m.eventSubscriber != nil {
m.eventSubscriber.Emit(ConnectionEvent{
Type: ConnectionEventNew,
ID: metadata.ID,
Metadata: metadata,
})
}
2022-07-19 22:16:49 +08:00
}
2024-06-11 21:16:33 +08:00
func (m *Manager) Leave(c Tracker) {
metadata := c.Metadata()
_, loaded := m.connections.LoadAndDelete(metadata.ID)
if loaded {
2026-01-17 19:15:40 +08:00
closedAt := time.Now()
metadata.ClosedAt = closedAt
metadataCopy := *metadata
2024-06-11 21:16:33 +08:00
m.closedConnectionsAccess.Lock()
2026-01-17 19:15:40 +08:00
if m.closedConnections.Len() >= closedConnectionsLimit {
2024-06-11 21:16:33 +08:00
m.closedConnections.PopFront()
}
2026-01-17 19:15:40 +08:00
m.closedConnections.PushBack(metadataCopy)
m.closedConnectionsAccess.Unlock()
if m.eventSubscriber != nil {
m.eventSubscriber.Emit(ConnectionEvent{
Type: ConnectionEventClosed,
ID: metadata.ID,
2026-01-17 19:15:40 +08:00
Metadata: &metadataCopy,
ClosedAt: closedAt,
})
}
2024-06-11 21:16:33 +08:00
}
2022-07-19 22:16:49 +08:00
}
func (m *Manager) PushUploaded(size int64) {
m.uploadTotal.Add(size)
}
func (m *Manager) PushDownloaded(size int64) {
m.downloadTotal.Add(size)
}
2023-07-16 14:08:45 +08:00
func (m *Manager) Total() (up int64, down int64) {
return m.uploadTotal.Load(), m.downloadTotal.Load()
}
2024-06-11 21:16:33 +08:00
func (m *Manager) ConnectionsLen() int {
2023-07-16 14:08:45 +08:00
return m.connections.Len()
}
func (m *Manager) Connections() []*TrackerMetadata {
var connections []*TrackerMetadata
2024-06-11 21:16:33 +08:00
m.connections.Range(func(_ uuid.UUID, value Tracker) bool {
connections = append(connections, value.Metadata())
return true
})
return connections
}
func (m *Manager) ClosedConnections() []*TrackerMetadata {
2024-06-11 21:16:33 +08:00
m.closedConnectionsAccess.Lock()
2026-01-17 19:15:40 +08:00
values := m.closedConnections.Array()
m.closedConnectionsAccess.Unlock()
if len(values) == 0 {
return nil
}
connections := make([]*TrackerMetadata, len(values))
for i := range values {
connections[i] = &values[i]
}
return connections
2024-06-11 21:16:33 +08:00
}
func (m *Manager) Connection(id uuid.UUID) Tracker {
connection, loaded := m.connections.Load(id)
if !loaded {
return nil
}
return connection
}
2022-07-19 22:16:49 +08:00
func (m *Manager) Snapshot() *Snapshot {
2024-06-11 21:16:33 +08:00
var connections []Tracker
m.connections.Range(func(_ uuid.UUID, value Tracker) bool {
if value.Metadata().OutboundType != C.TypeDNS {
connections = append(connections, value)
}
2022-07-19 22:16:49 +08:00
return true
})
2023-04-11 16:43:45 +08:00
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
m.memory = memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased
2022-07-19 22:16:49 +08:00
return &Snapshot{
2024-06-11 21:16:33 +08:00
Upload: m.uploadTotal.Load(),
Download: m.downloadTotal.Load(),
Connections: connections,
Memory: m.memory,
2022-07-19 22:16:49 +08:00
}
}
func (m *Manager) ResetStatistic() {
m.uploadTotal.Store(0)
m.downloadTotal.Store(0)
}
type Snapshot struct {
2024-06-11 21:16:33 +08:00
Download int64
Upload int64
Connections []Tracker
Memory uint64
}
func (s *Snapshot) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"downloadTotal": s.Download,
"uploadTotal": s.Upload,
"connections": common.Map(s.Connections, func(t Tracker) *TrackerMetadata { return t.Metadata() }),
2024-06-11 21:16:33 +08:00
"memory": s.Memory,
})
2022-07-19 22:16:49 +08:00
}