CI / test (push) Successful in 2m32s
Routes Java Edition connections by the hostname in the handshake, with wildcard domains, backend failover, PROXY protocol v2, cached server-list status from health probes, hot-reloaded config, and an admin page and metrics endpoint. Includes CI, tagged releases and a container image. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
package proxy
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type serverStats struct {
|
|
active atomic.Int64
|
|
total atomic.Int64
|
|
failures atomic.Int64
|
|
bytesIn atomic.Int64 // client -> backend
|
|
bytesOut atomic.Int64 // backend -> client
|
|
}
|
|
|
|
type Stats struct {
|
|
Started time.Time
|
|
active atomic.Int64
|
|
total atomic.Int64
|
|
rejected atomic.Int64
|
|
unknown atomic.Int64
|
|
mu sync.Mutex
|
|
servers map[string]*serverStats
|
|
}
|
|
|
|
func newStats() *Stats {
|
|
return &Stats{Started: time.Now(), servers: map[string]*serverStats{}}
|
|
}
|
|
|
|
func (s *Stats) server(name string) *serverStats {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
ss := s.servers[name]
|
|
if ss == nil {
|
|
ss = &serverStats{}
|
|
s.servers[name] = ss
|
|
}
|
|
return ss
|
|
}
|
|
|
|
type ServerSnapshot struct {
|
|
Name string `json:"name"`
|
|
Active int64 `json:"active"`
|
|
Total int64 `json:"total"`
|
|
Failures int64 `json:"failures"`
|
|
BytesIn int64 `json:"bytes_in"`
|
|
BytesOut int64 `json:"bytes_out"`
|
|
}
|
|
|
|
type Snapshot struct {
|
|
UptimeSecs int64 `json:"uptime_secs"`
|
|
Active int64 `json:"active"`
|
|
Total int64 `json:"total"`
|
|
Rejected int64 `json:"rejected"`
|
|
Unknown int64 `json:"unknown_host"`
|
|
Servers []ServerSnapshot `json:"servers"`
|
|
}
|
|
|
|
func (s *Stats) Snapshot() Snapshot {
|
|
out := Snapshot{
|
|
UptimeSecs: int64(time.Since(s.Started).Seconds()),
|
|
Active: s.active.Load(), Total: s.total.Load(),
|
|
Rejected: s.rejected.Load(), Unknown: s.unknown.Load(),
|
|
}
|
|
s.mu.Lock()
|
|
for n, ss := range s.servers {
|
|
out.Servers = append(out.Servers, ServerSnapshot{n, ss.active.Load(), ss.total.Load(),
|
|
ss.failures.Load(), ss.bytesIn.Load(), ss.bytesOut.Load()})
|
|
}
|
|
s.mu.Unlock()
|
|
sort.Slice(out.Servers, func(i, j int) bool { return out.Servers[i].Name < out.Servers[j].Name })
|
|
return out
|
|
}
|