Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

Commit 715f9d5

Browse files
authored
refactor(server): Optimize connection pools (#31)
* Use a map to track pool for subdomain
1 parent e54ffad commit 715f9d5

File tree

2 files changed

+29
-50
lines changed

2 files changed

+29
-50
lines changed

internal/server/tunnel/pool.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (pool *Pool) Clean() {
7878
if idle > pool.size {
7979
// We have enough idle connections in the pool.
8080
// Terminate the connection if it is idle since more that IdleTimeout
81-
if int(time.Now().Sub(connection.idleSince).Seconds())*1000 > pool.server.Config.IdleTimeout {
81+
if int(time.Since(connection.idleSince).Seconds())*1000 > pool.server.Config.IdleTimeout {
8282
connection.close()
8383
}
8484
}

internal/server/tunnel/server.go

+28-49
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"log"
77
"math/rand"
88
"net/url"
9-
"reflect"
109
"strings"
1110
"sync"
1211
"time"
@@ -23,7 +22,7 @@ type Server struct {
2322
Upgrader websocket.Upgrader
2423

2524
// In Pools, keep connections with WebSocket peers.
26-
Pools []*Pool
25+
Pools map[string]*Pool
2726

2827
// A RWMutex is a reader/writer mutual exclusion Lock,
2928
// and it is for exclusive control with pools operation.
@@ -72,6 +71,7 @@ func NewServer(configFile string) (server *Server) {
7271
server = new(Server)
7372
server.Config = config
7473
server.Upgrader = websocket.Upgrader{}
74+
server.Pools = make(map[string]*Pool)
7575

7676
server.done = make(chan struct{})
7777
server.Dispatcher = make(chan *ConnectionRequest)
@@ -111,13 +111,13 @@ func (s *Server) clean() {
111111
idle := 0
112112
busy := 0
113113

114-
var pools []*Pool
115-
for _, pool := range s.Pools {
114+
pools := make(map[string]*Pool)
115+
for subdomain, pool := range s.Pools {
116116
if pool.IsEmpty() {
117117
log.Printf("Removing empty connection pool : %s", pool.ID)
118118
pool.Shutdown()
119119
} else {
120-
pools = append(pools, pool)
120+
pools[subdomain] = pool
121121
}
122122

123123
ps := pool.Size()
@@ -156,36 +156,25 @@ func (s *Server) DispatchConnections() {
156156
}
157157

158158
s.Lock.RLock()
159-
if len(s.Pools) == 0 {
159+
pool, ok := s.Pools[request.Subdomain]
160+
if !ok {
160161
// No connection pool available
161162
s.Lock.RUnlock()
162163
break
163164
}
164165

165-
// [1]: Select a pool which has an idle connection
166-
// Build a select statement dynamically to handle an arbitrary number of pools.
167-
cases := make([]reflect.SelectCase, len(s.Pools)+1)
168-
for i, ch := range s.Pools {
169-
// Pick connection for the requested subdomain
170-
if ch.Subdomain != request.Subdomain {
171-
cases[i] = reflect.SelectCase{
172-
Dir: reflect.SelectRecv,
173-
Chan: reflect.ValueOf(nil)}
174-
continue
166+
var connection *Connection
167+
for _, conn := range pool.connections {
168+
if conn.status == Idle {
169+
connection = conn
170+
break
175171
}
176-
cases[i] = reflect.SelectCase{
177-
Dir: reflect.SelectRecv,
178-
Chan: reflect.ValueOf(ch.idle)}
179172
}
180-
cases[len(cases)-1] = reflect.SelectCase{
181-
Dir: reflect.SelectDefault}
182173
s.Lock.RUnlock()
183174

184-
_, value, ok := reflect.Select(cases)
185-
if !ok {
186-
continue // a pool has been removed, try again
175+
if connection == nil {
176+
continue
187177
}
188-
connection, _ := value.Interface().(*Connection)
189178

190179
// [2]: Verify that we can use this connection and take it.
191180
if connection.Take() {
@@ -233,37 +222,27 @@ func (s *Server) Shutdown() {
233222
}
234223

235224
func (s *Server) GetOrCreatePoolForUser(subdomain, localServer, userIdentifier string, id PoolID) (*Pool, error) {
236-
var pool *Pool
237225
// There is no need to create a new pool,
238226
// if it is already registered in current pools.
239-
for _, p := range s.Pools {
240-
if p.Subdomain == subdomain {
241-
if p.ID == id {
242-
pool = p
243-
break
244-
} else {
245-
// Pool exist for the subdomain, but for different user
246-
return nil, fmt.Errorf("subdomain already in use")
247-
}
248-
}
227+
p, ok := s.Pools[subdomain]
228+
if !ok {
229+
pool := NewPool(s, id, subdomain, localServer, userIdentifier)
230+
s.Pools[subdomain] = pool
231+
return pool, nil
249232
}
250-
if pool == nil {
251-
// Create new pool, if no pools exist for the user
252-
pool = NewPool(s, id, subdomain, localServer, userIdentifier)
253-
s.Pools = append(s.Pools, pool)
233+
234+
if p.ID != id {
235+
return nil, fmt.Errorf("subdomain already in use")
254236
}
255-
return pool, nil
237+
238+
return p, nil
256239
}
257240

258241
func (s *Server) GetDestinationURL(subdomain string) string {
259-
var dstURL string
260-
261-
for _, p := range s.Pools {
262-
if p.Subdomain == subdomain {
263-
dstURL = p.LocalServer
264-
break
265-
}
242+
p, ok := s.Pools[subdomain]
243+
if !ok {
244+
return ""
266245
}
267246

268-
return dstURL
247+
return p.LocalServer
269248
}

0 commit comments

Comments
 (0)