122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package upstream
|
|
|
|
import (
|
|
"cmpp-platform/gateway/internal/queue"
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type connectionPool struct {
|
|
channelID string
|
|
connectionID string
|
|
config queue.UpstreamConfig
|
|
apiBaseURL string
|
|
httpClient *http.Client
|
|
reporter func(context.Context, ConnectionState) error
|
|
|
|
mu sync.Mutex
|
|
connectMu sync.Mutex
|
|
conns []*connection
|
|
next int
|
|
reconnectSignal chan struct{}
|
|
stopCh chan struct{}
|
|
stopOnce sync.Once
|
|
supervisorOnce sync.Once
|
|
reconnectCount int
|
|
lastReconnectAttemptAt time.Time
|
|
nextReconnectAt time.Time
|
|
lastErrorCategory string
|
|
}
|
|
|
|
func (p *connectionPool) matches(config queue.UpstreamConfig) bool {
|
|
return p.config == normalizeUpstreamConfig(config)
|
|
}
|
|
|
|
func (p *connectionPool) ensureConnected() error {
|
|
p.connectMu.Lock()
|
|
defer p.connectMu.Unlock()
|
|
desired := p.config.DesiredConnections
|
|
if desired <= 0 {
|
|
desired = 1
|
|
}
|
|
connectedAny := false
|
|
for {
|
|
p.mu.Lock()
|
|
active := p.conns[:0]
|
|
for _, existing := range p.conns {
|
|
existing.mu.Lock()
|
|
usable := existing.client != nil && !existing.closed
|
|
existing.mu.Unlock()
|
|
if usable {
|
|
active = append(active, existing)
|
|
}
|
|
}
|
|
p.conns = active
|
|
if len(p.conns) >= desired {
|
|
p.mu.Unlock()
|
|
break
|
|
}
|
|
index := len(p.conns)
|
|
conn := &connection{
|
|
channelID: p.channelID,
|
|
config: p.config,
|
|
index: index,
|
|
pool: p,
|
|
apiBaseURL: p.apiBaseURL,
|
|
httpClient: p.httpClient,
|
|
window: make(chan struct{}, p.config.WindowSize),
|
|
pending: make(map[uint32]chan submitPartResponse),
|
|
tracker: make(map[uint64]queue.SubmitCommand),
|
|
longUplink: make(map[string]*longUplinkAssembly),
|
|
heartbeatPending: make(map[uint32]time.Time),
|
|
}
|
|
p.mu.Unlock()
|
|
|
|
connected, err := conn.ensureConnected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.mu.Lock()
|
|
p.conns = append(p.conns, conn)
|
|
p.mu.Unlock()
|
|
connectedAny = connectedAny || connected
|
|
}
|
|
if connectedAny {
|
|
_ = p.reportState(context.Background(), "connected", nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *connectionPool) close() {
|
|
p.connectMu.Lock()
|
|
defer p.connectMu.Unlock()
|
|
p.stopOnce.Do(func() {
|
|
close(p.stopCh)
|
|
})
|
|
p.mu.Lock()
|
|
conns := p.conns
|
|
p.conns = nil
|
|
p.mu.Unlock()
|
|
for _, conn := range conns {
|
|
conn.close()
|
|
}
|
|
}
|
|
|
|
func (p *connectionPool) countActiveConnections() int {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
count := 0
|
|
for _, conn := range p.conns {
|
|
conn.mu.Lock()
|
|
active := conn.client != nil && !conn.closed
|
|
conn.mu.Unlock()
|
|
if active {
|
|
count += 1
|
|
}
|
|
}
|
|
return count
|
|
}
|