-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmonitor.go
More file actions
118 lines (110 loc) · 2.78 KB
/
Copy pathmonitor.go
File metadata and controls
118 lines (110 loc) · 2.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"context"
"net"
"sort"
"sync"
"time"
)
// snapshot holds one window of monitoring results for a zone.
type snapshot struct {
checks int
success int
latencies []float64 // ms, successful lookups only
targetsUp int
targetsDown int
}
func percentile(sorted []float64, p float64) float64 {
if len(sorted) == 0 {
return 0
}
idx := int(p / 100 * float64(len(sorted)-1))
if idx < 0 {
idx = 0
}
if idx >= len(sorted) {
idx = len(sorted) - 1
}
return sorted[idx]
}
// resolverFor returns a *net.Resolver that queries a specific DNS server.
func resolverFor(server string) *net.Resolver {
return &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
d := net.Dialer{Timeout: 5 * time.Second}
return d.DialContext(ctx, network, net.JoinHostPort(server, "53"))
},
}
}
// monitorZone resolves the apex across resolvers (measuring latency) and dials the
// resolved targets on :443 to gauge reachability. Lookups and dials run concurrently.
func monitorZone(ctx context.Context, zone string, resolvers []string, dialTimeout time.Duration) snapshot {
var s snapshot
var ips []string
var mu sync.Mutex
var wg sync.WaitGroup
for _, server := range resolvers {
wg.Add(1)
go func(server string) {
defer wg.Done()
r := resolverFor(server)
start := time.Now()
lookupCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
addrs, err := r.LookupHost(lookupCtx, zone)
cancel()
mu.Lock()
defer mu.Unlock()
s.checks++
if err != nil || len(addrs) == 0 {
return
}
s.success++
s.latencies = append(s.latencies, float64(time.Since(start).Microseconds())/1000.0)
if len(ips) == 0 {
ips = addrs
}
}(server)
}
wg.Wait()
// Target reachability: dial each resolved IP on 443 concurrently.
var dwg sync.WaitGroup
for _, ip := range ips {
dwg.Add(1)
go func(ip string) {
defer dwg.Done()
conn, err := net.DialTimeout("tcp", net.JoinHostPort(ip, "443"), dialTimeout)
mu.Lock()
defer mu.Unlock()
if err != nil {
s.targetsDown++
return
}
_ = conn.Close()
s.targetsUp++
}(ip)
}
dwg.Wait()
return s
}
func (s snapshot) toPayload(zone, region, agentID string, start, end time.Time) metricPayload {
successRate := 0.0
if s.checks > 0 {
successRate = float64(s.success) / float64(s.checks) * 100
}
sorted := append([]float64(nil), s.latencies...)
sort.Float64s(sorted)
return metricPayload{
ZoneName: zone,
Region: region,
AgentID: agentID,
WindowStart: start.UTC().Format(time.RFC3339),
WindowEnd: end.UTC().Format(time.RFC3339),
Checks: s.checks,
SuccessRate: successRate,
LatencyP50: percentile(sorted, 50),
LatencyP95: percentile(sorted, 95),
TargetsUp: s.targetsUp,
TargetsDown: s.targetsDown,
}
}