Skip to content

Commit 5c75727

Browse files
committed
Address review comments.
1 parent 959de84 commit 5c75727

File tree

9 files changed

+169
-211
lines changed

9 files changed

+169
-211
lines changed

cmd/outline-ss-server/metrics.go

Lines changed: 56 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,15 @@ import (
2626
"github.com/prometheus/client_golang/prometheus"
2727
)
2828

29-
const (
30-
// How often to report the active IP key TunnelTime.
31-
activeIPKeyTrackerReportingInterval = 5 * time.Second
32-
)
29+
// How often to report the active IP key TunnelTime.
30+
const tunnelTimeTrackerReportingInterval = 5 * time.Second
3331

34-
var since = time.Since
32+
// Now is stubbable for testing.
33+
var Now = time.Now
3534

3635
type outlineMetrics struct {
3736
ipinfo.IPInfoMap
38-
activeIPKeyTracker
37+
tunnelTimeTracker
3938

4039
buildInfo *prometheus.GaugeVec
4140
accessKeys prometheus.Gauge
@@ -45,8 +44,8 @@ type outlineMetrics struct {
4544
timeToCipherMs *prometheus.HistogramVec
4645
// TODO: Add time to first byte.
4746

48-
IPKeyTimePerKey *prometheus.CounterVec
49-
IPKeyTimePerLocation *prometheus.CounterVec
47+
TunnelTimePerKey *prometheus.CounterVec
48+
TunnelTimePerLocation *prometheus.CounterVec
5049

5150
tcpProbes *prometheus.HistogramVec
5251
tcpOpenConnections *prometheus.CounterVec
@@ -61,93 +60,89 @@ type outlineMetrics struct {
6160
var _ service.TCPMetrics = (*outlineMetrics)(nil)
6261
var _ service.UDPMetrics = (*outlineMetrics)(nil)
6362

63+
type ReportTunnelTimeFunc func(IPKey, ipinfo.IPInfo, time.Duration)
64+
6465
type activeClient struct {
6566
IPKey IPKey
67+
clientInfo ipinfo.IPInfo
6668
connectionCount int
6769
startTime time.Time
6870
}
6971

70-
func (c *activeClient) IsActive() bool {
71-
return c.connectionCount > 0
72-
}
73-
7472
type IPKey struct {
7573
ip string
7674
accessKey string
7775
}
7876

79-
type activeIPKeyTracker struct {
80-
activeClients map[IPKey]activeClient
81-
metricsCallback func(IPKey, time.Duration)
77+
type tunnelTimeTracker struct {
78+
activeClients map[IPKey]activeClient
79+
reportTunnelTime ReportTunnelTimeFunc
8280
}
8381

8482
// Reports time connected for all active clients, called at a regular interval.
85-
func (t *activeIPKeyTracker) reportAll() {
83+
func (t *tunnelTimeTracker) reportAll(now time.Time) {
8684
if len(t.activeClients) == 0 {
8785
logger.Debugf("No active clients. No IPKey activity to report.")
8886
return
8987
}
9088
for _, c := range t.activeClients {
91-
t.reportDuration(c)
89+
t.reportDuration(c, now)
9290
}
9391
}
9492

9593
// Reports time connected for a given active client.
96-
func (t *activeIPKeyTracker) reportDuration(c activeClient) {
97-
connDuration := since(c.startTime)
94+
func (t *tunnelTimeTracker) reportDuration(c activeClient, now time.Time) {
95+
connDuration := now.Sub(c.startTime)
9896
logger.Debugf("Reporting activity for key `%v`, duration: %v", c.IPKey.accessKey, connDuration)
99-
t.metricsCallback(c.IPKey, connDuration)
97+
t.reportTunnelTime(c.IPKey, c.clientInfo, connDuration)
10098

10199
// Reset the start time now that it's been reported.
102-
c.startTime = time.Now()
100+
c.startTime = Now()
103101
t.activeClients[c.IPKey] = c
104102
}
105103

106104
// Registers a new active connection for a client [net.Addr] and access key.
107-
func (t *activeIPKeyTracker) startConnection(addr net.Addr, accessKey string) {
108-
hostname, _, _ := net.SplitHostPort(addr.String())
105+
func (t *tunnelTimeTracker) startConnection(clientInfo ipinfo.IPInfo, clientAddr net.Addr, accessKey string) {
106+
hostname, _, _ := net.SplitHostPort(clientAddr.String())
109107
ipKey := IPKey{ip: hostname, accessKey: accessKey}
110108

111109
c, exists := t.activeClients[ipKey]
112110
if !exists {
113-
c = activeClient{ipKey, 0, time.Now()}
111+
c = activeClient{ipKey, clientInfo, 0, Now()}
114112
}
115113
c.connectionCount++
116114
t.activeClients[ipKey] = c
117115
}
118116

119117
// Removes an active connection for a client [net.Addr] and access key.
120-
func (t *activeIPKeyTracker) stopConnection(addr net.Addr, accessKey string) {
121-
hostname, _, _ := net.SplitHostPort(addr.String())
118+
func (t *tunnelTimeTracker) stopConnection(clientAddr net.Addr, accessKey string) {
119+
hostname, _, _ := net.SplitHostPort(clientAddr.String())
122120
ipKey := IPKey{ip: hostname, accessKey: accessKey}
123121

124122
c := t.activeClients[ipKey]
123+
c, exists := t.activeClients[ipKey]
124+
if !exists {
125+
logger.Warningf("Failed to find active client")
126+
return
127+
}
125128
c.connectionCount--
126-
if !c.IsActive() {
127-
t.reportDuration(c)
129+
if c.connectionCount <= 0 {
130+
t.reportDuration(c, Now())
128131
delete(t.activeClients, ipKey)
129132
return
130133
}
131134
t.activeClients[ipKey] = c
132135
}
133136

134-
func newActiveIPKeyTracker(callback func(IPKey, time.Duration)) *activeIPKeyTracker {
135-
t := &activeIPKeyTracker{activeClients: make(map[IPKey]activeClient), metricsCallback: callback}
136-
ticker := time.NewTicker(activeIPKeyTrackerReportingInterval)
137-
done := make(chan struct{})
137+
func newTunnelTimeTracker(report ReportTunnelTimeFunc) *tunnelTimeTracker {
138+
tracker := &tunnelTimeTracker{activeClients: make(map[IPKey]activeClient), reportTunnelTime: report}
139+
ticker := time.NewTicker(tunnelTimeTrackerReportingInterval)
138140
go func() {
139-
for {
140-
select {
141-
case <-ticker.C:
142-
t.reportAll()
143-
case <-done:
144-
logger.Debugf("done channel %p closed", done)
145-
ticker.Stop()
146-
return
147-
}
141+
for t := range ticker.C {
142+
tracker.reportAll(t)
148143
}
149144
}()
150-
return t
145+
return tracker
151146
}
152147

153148
// newPrometheusOutlineMetrics constructs a metrics object that uses
@@ -205,14 +200,14 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
205200
float64(7 * 24 * time.Hour.Milliseconds()), // Week
206201
},
207202
}, []string{"status"}),
208-
IPKeyTimePerKey: prometheus.NewCounterVec(prometheus.CounterOpts{
203+
TunnelTimePerKey: prometheus.NewCounterVec(prometheus.CounterOpts{
209204
Namespace: "shadowsocks",
210-
Name: "ip_key_connectivity_seconds",
205+
Name: "tunnel_time_seconds",
211206
Help: "Time at least 1 connection was open for a (IP, access key) pair, per key",
212207
}, []string{"access_key"}),
213-
IPKeyTimePerLocation: prometheus.NewCounterVec(prometheus.CounterOpts{
208+
TunnelTimePerLocation: prometheus.NewCounterVec(prometheus.CounterOpts{
214209
Namespace: "shadowsocks",
215-
Name: "ip_key_connectivity_seconds_per_location",
210+
Name: "tunnel_time_seconds_per_location",
216211
Help: "Time at least 1 connection was open for a (IP, access key) pair, per location",
217212
}, []string{"location", "asn"}),
218213
dataBytes: prometheus.NewCounterVec(
@@ -256,12 +251,12 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
256251
Help: "Entries removed from the UDP NAT table",
257252
}),
258253
}
259-
m.activeIPKeyTracker = *newActiveIPKeyTracker(m.reportIPKeyActivity)
254+
m.tunnelTimeTracker = *newTunnelTimeTracker(m.addTunnelTime)
260255

261256
// TODO: Is it possible to pass where to register the collectors?
262257
registerer.MustRegister(m.buildInfo, m.accessKeys, m.ports, m.tcpProbes, m.tcpOpenConnections, m.tcpClosedConnections, m.tcpConnectionDurationMs,
263258
m.dataBytes, m.dataBytesPerLocation, m.timeToCipherMs, m.udpPacketsFromClientPerLocation, m.udpAddedNatEntries, m.udpRemovedNatEntries,
264-
m.IPKeyTimePerKey, m.IPKeyTimePerLocation)
259+
m.TunnelTimePerKey, m.TunnelTimePerLocation)
265260
return m
266261
}
267262

@@ -274,28 +269,18 @@ func (m *outlineMetrics) SetNumAccessKeys(numKeys int, ports int) {
274269
m.ports.Set(float64(ports))
275270
}
276271

277-
func (m *outlineMetrics) AddOpenTCPConnection(addr net.Addr) {
278-
clientInfo, err := ipinfo.GetIPInfoFromAddr(m.IPInfoMap, addr)
279-
if err != nil {
280-
logger.Warningf("Failed client info lookup: %v", err)
281-
}
282-
logger.Debugf("Got info \"%#v\" for IP %v", clientInfo, addr.String())
272+
func (m *outlineMetrics) AddOpenTCPConnection(clientInfo ipinfo.IPInfo) {
283273
m.tcpOpenConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN)).Inc()
284274
}
285275

286-
// Reports total time connected, by access key and by country.
287-
func (m *outlineMetrics) reportIPKeyActivity(ipKey IPKey, duration time.Duration) {
288-
m.IPKeyTimePerKey.WithLabelValues(ipKey.accessKey).Add(duration.Seconds())
289-
ip := net.ParseIP(ipKey.ip)
290-
clientInfo, err := ipinfo.GetIPInfoFromIP(m.IPInfoMap, ip)
291-
if err != nil {
292-
logger.Warningf("Failed client info lookup: %v", err)
293-
}
294-
m.IPKeyTimePerLocation.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN)).Add(duration.Seconds())
276+
// Reports total time connected (i.e. TunnelTime), by access key and by country.
277+
func (m *outlineMetrics) addTunnelTime(ipKey IPKey, clientInfo ipinfo.IPInfo, duration time.Duration) {
278+
m.TunnelTimePerKey.WithLabelValues(ipKey.accessKey).Add(duration.Seconds())
279+
m.TunnelTimePerLocation.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN)).Add(duration.Seconds())
295280
}
296281

297-
func (m *outlineMetrics) AddAuthenticatedTCPConnection(addr net.Addr, accessKey string) {
298-
m.activeIPKeyTracker.startConnection(addr, accessKey)
282+
func (m *outlineMetrics) AddAuthenticatedTCPConnection(clientInfo ipinfo.IPInfo, clientAddr net.Addr, accessKey string) {
283+
m.tunnelTimeTracker.startConnection(clientInfo, clientAddr, accessKey)
299284
}
300285

301286
// addIfNonZero helps avoid the creation of series that are always zero.
@@ -312,12 +297,7 @@ func asnLabel(asn int) string {
312297
return fmt.Sprint(asn)
313298
}
314299

315-
func (m *outlineMetrics) AddClosedTCPConnection(addr net.Addr, accessKey, status string, data metrics.ProxyMetrics, duration time.Duration) {
316-
clientInfo, err := ipinfo.GetIPInfoFromAddr(m.IPInfoMap, addr)
317-
if err != nil {
318-
logger.Warningf("Failed client info lookup: %v", err)
319-
}
320-
logger.Debugf("Got info \"%#v\" for IP %v", clientInfo, addr.String())
300+
func (m *outlineMetrics) AddClosedTCPConnection(clientInfo ipinfo.IPInfo, clientAddr net.Addr, accessKey, status string, data metrics.ProxyMetrics, duration time.Duration) {
321301
m.tcpClosedConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN), status, accessKey).Inc()
322302
m.tcpConnectionDurationMs.WithLabelValues(status).Observe(duration.Seconds() * 1000)
323303
addIfNonZero(data.ClientProxy, m.dataBytes, "c>p", "tcp", accessKey)
@@ -329,7 +309,7 @@ func (m *outlineMetrics) AddClosedTCPConnection(addr net.Addr, accessKey, status
329309
addIfNonZero(data.ProxyClient, m.dataBytes, "c<p", "tcp", accessKey)
330310
addIfNonZero(data.ProxyClient, m.dataBytesPerLocation, "c<p", "tcp", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
331311

332-
m.activeIPKeyTracker.stopConnection(addr, accessKey)
312+
m.tunnelTimeTracker.stopConnection(clientAddr, accessKey)
333313
}
334314

335315
func (m *outlineMetrics) AddUDPPacketFromClient(clientInfo ipinfo.IPInfo, accessKey, status string, clientProxyBytes, proxyTargetBytes int) {
@@ -347,16 +327,16 @@ func (m *outlineMetrics) AddUDPPacketFromTarget(clientInfo ipinfo.IPInfo, access
347327
addIfNonZero(int64(proxyClientBytes), m.dataBytesPerLocation, "c<p", "udp", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
348328
}
349329

350-
func (m *outlineMetrics) AddUDPNatEntry(addr net.Addr, accessKey string) {
330+
func (m *outlineMetrics) AddUDPNatEntry(clientInfo ipinfo.IPInfo, clientAddr net.Addr, accessKey string) {
351331
m.udpAddedNatEntries.Inc()
352332

353-
m.activeIPKeyTracker.startConnection(addr, accessKey)
333+
m.tunnelTimeTracker.startConnection(clientInfo, clientAddr, accessKey)
354334
}
355335

356-
func (m *outlineMetrics) RemoveUDPNatEntry(addr net.Addr, accessKey string) {
336+
func (m *outlineMetrics) RemoveUDPNatEntry(clientInfo ipinfo.IPInfo, clientAddr net.Addr, accessKey string) {
357337
m.udpRemovedNatEntries.Inc()
358338

359-
m.activeIPKeyTracker.stopConnection(addr, accessKey)
339+
m.tunnelTimeTracker.stopConnection(clientAddr, accessKey)
360340
}
361341

362342
func (m *outlineMetrics) AddTCPProbe(status, drainResult string, port int, clientProxyBytes int64) {

0 commit comments

Comments
 (0)