@@ -21,6 +21,36 @@ type PerformanceClient struct {
2121 ctx context.Context
2222 cancel context.CancelFunc
2323 mutex sync.RWMutex
24+ closed bool
25+ }
26+
27+ func (c * PerformanceClient ) trySend (message interface {}) bool {
28+ c .mutex .RLock ()
29+ if c .closed {
30+ c .mutex .RUnlock ()
31+ return false
32+ }
33+
34+ select {
35+ case c .send <- message :
36+ c .mutex .RUnlock ()
37+ return true
38+ default :
39+ c .mutex .RUnlock ()
40+ return false
41+ }
42+ }
43+
44+ func (c * PerformanceClient ) closeSendChannel () {
45+ c .mutex .Lock ()
46+ if c .closed {
47+ c .mutex .Unlock ()
48+ return
49+ }
50+
51+ close (c .send )
52+ c .closed = true
53+ c .mutex .Unlock ()
2454}
2555
2656// PerformanceHub manages WebSocket connections for Nginx performance monitoring
@@ -60,20 +90,18 @@ func (h *PerformanceHub) run() {
6090 case client := <- h .register :
6191 h .mutex .Lock ()
6292 h .clients [client ] = true
93+ currentClients := len (h .clients )
6394 h .mutex .Unlock ()
64- logger .Debug ("Nginx performance client connected, total clients:" , len ( h . clients ) )
95+ logger .Debug ("Nginx performance client connected, total clients:" , currentClients )
6596
6697 // Send initial data to the new client
6798 go h .sendPerformanceDataToClient (client )
6899
69100 case client := <- h .unregister :
70- h .mutex .Lock ()
71- if _ , ok := h .clients [client ]; ok {
72- delete (h .clients , client )
73- close (client .send )
101+ currentClients , removed := h .removeClient (client )
102+ if removed {
103+ logger .Debug ("Nginx performance client disconnected, total clients:" , currentClients )
74104 }
75- h .mutex .Unlock ()
76- logger .Debug ("Nginx performance client disconnected, total clients:" , len (h .clients ))
77105
78106 case <- h .ticker .C :
79107 // Send performance data to all connected clients
@@ -82,52 +110,76 @@ func (h *PerformanceHub) run() {
82110 case <- kernel .Context .Done ():
83111 logger .Debug ("PerformanceHub: Context cancelled, closing WebSocket" )
84112 // Shutdown all clients
85- h .mutex .Lock ()
86- for client := range h .clients {
87- close (client .send )
88- delete (h .clients , client )
113+ for _ , client := range h .activeClients () {
114+ h .removeClient (client )
89115 }
90- h .mutex .Unlock ()
91116 return
92117 }
93118 }
94119}
95120
121+ func (h * PerformanceHub ) activeClients () []* PerformanceClient {
122+ h .mutex .RLock ()
123+ if len (h .clients ) == 0 {
124+ h .mutex .RUnlock ()
125+ return nil
126+ }
127+
128+ clients := make ([]* PerformanceClient , 0 , len (h .clients ))
129+ for client := range h .clients {
130+ clients = append (clients , client )
131+ }
132+ h .mutex .RUnlock ()
133+ return clients
134+ }
135+
136+ func (h * PerformanceHub ) removeClient (client * PerformanceClient ) (remaining int , removed bool ) {
137+ h .mutex .Lock ()
138+ _ , removed = h .clients [client ]
139+ if removed {
140+ delete (h .clients , client )
141+ }
142+ remaining = len (h .clients )
143+ h .mutex .Unlock ()
144+
145+ if removed {
146+ client .closeSendChannel ()
147+ }
148+ return remaining , removed
149+ }
150+
96151// sendPerformanceDataToClient sends performance data to a specific client
97152func (h * PerformanceHub ) sendPerformanceDataToClient (client * PerformanceClient ) {
98- response := performance .GetPerformanceData ()
99-
100153 select {
101- case client .send <- response :
154+ case <- client .ctx .Done ():
155+ return
102156 default :
157+ }
158+
159+ response := performance .GetPerformanceData ()
160+
161+ if ! client .trySend (response ) {
103162 // Channel is full, remove client
104163 h .unregister <- client
105164 }
106165}
107166
108167// broadcastPerformanceData sends performance data to all connected clients
109168func (h * PerformanceHub ) broadcastPerformanceData () {
110- h .mutex .RLock ()
111-
112- // Check if there are any connected clients
113- if len (h .clients ) == 0 {
114- h .mutex .RUnlock ()
169+ clients := h .activeClients ()
170+ if len (clients ) == 0 {
115171 return
116172 }
117173
118- // Only get performance data if there are connected clients
119174 response := performance .GetPerformanceData ()
120175
121- for client := range h .clients {
122- select {
123- case client .send <- response :
124- default :
125- // Channel is full, remove client
126- close (client .send )
127- delete (h .clients , client )
176+ for _ , client := range clients {
177+ if client .trySend (response ) {
178+ continue
128179 }
180+
181+ h .removeClient (client )
129182 }
130- h .mutex .RUnlock ()
131183}
132184
133185// WebSocket upgrader configuration
0 commit comments