/
main.go
122 lines (97 loc) · 3.8 KB
/
main.go
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
119
120
121
122
package main
import (
"fmt"
"k8s.io/klog/v2"
"log"
"math"
"net/http"
"net/http/httputil"
"net/url"
"os"
"time"
"github.com/asecurityteam/rolling"
"github.com/lterrac/system-autoscaler/pkg/metrics-exposer/pkg/metrics"
)
var target = &url.URL{}
var window = &rolling.TimePolicy{}
// Environment
var address string
var port string
var windowSize time.Duration
var windowGranularity time.Duration
func main() {
mux := http.NewServeMux()
mux.Handle("/metric/response_time", http.HandlerFunc(ResponseTime))
mux.Handle("/metric/request_count", http.HandlerFunc(RequestCount))
mux.Handle("/metric/throughput", http.HandlerFunc(Throughput))
mux.Handle("/metrics/", http.HandlerFunc(AllMetrics))
mux.Handle("/", http.HandlerFunc(ForwardRequest))
address = os.Getenv("ADDRESS")
port = os.Getenv("PORT")
windowSizeString := os.Getenv("WINDOW_SIZE")
windowGranularityString := os.Getenv("WINDOW_GRANULARITY")
var err error
log.Println("Reading environment variables")
srv := &http.Server{
Addr: ":8000",
Handler: mux,
}
target, _ = url.Parse("http://" + address + ":" + port)
log.Println("Forwarding all requests to:", target)
windowSize, err = time.ParseDuration(windowSizeString)
if err != nil {
log.Fatalf("Failed to parse windows size. Error: %v", err)
}
windowGranularity, err = time.ParseDuration(windowGranularityString)
if err != nil {
log.Fatalf("Failed to parse windows granularity. Error: %v", err)
}
window = rolling.NewTimePolicy(rolling.NewWindow(int(windowSize.Nanoseconds()/windowGranularity.Nanoseconds())), time.Millisecond)
log.Println("Time window initialized with size:", windowSizeString, " and granularity:", windowGranularityString)
// output error and quit if ListenAndServe fails
log.Fatal(srv.ListenAndServe())
}
// ForwardRequest send all the request the the pod except for the ones having metrics/ in the path
func ForwardRequest(res http.ResponseWriter, req *http.Request) {
requestTime := time.Now()
httputil.NewSingleHostReverseProxy(target).ServeHTTP(res, req)
responseTime := time.Now()
delta := responseTime.Sub(requestTime)
window.Append(float64(delta.Milliseconds()))
}
// ResponseTime return the pod average response time
func ResponseTime(res http.ResponseWriter, req *http.Request) {
responseTime := window.Reduce(rolling.Avg)
if math.IsNaN(responseTime) {
responseTime = 0
}
_, _ = fmt.Fprintf(res, `{"%s": %f}`, metrics.ResponseTime.String(), responseTime)
}
// RequestCount return the current number of request sent to the pod
func RequestCount(res http.ResponseWriter, req *http.Request) {
requestCount := window.Reduce(rolling.Count)
if math.IsNaN(requestCount) {
requestCount = 0
}
_, _ = fmt.Fprintf(res, `{"%s": %f}`, metrics.RequestCount.String(), requestCount)
}
// Throughput returns the pod throughput in request per second
func Throughput(res http.ResponseWriter, req *http.Request) {
throughput := window.Reduce(rolling.Count) / windowSize.Seconds()
_, _ = fmt.Fprintf(res, `{"%s": %f}`, metrics.Throughput.String(), throughput)
}
// AllMetrics returns all the metrics available for the pod
func AllMetrics(res http.ResponseWriter, req *http.Request) {
responseTime := window.Reduce(rolling.Avg)
if math.IsNaN(responseTime) {
responseTime = 0
}
requestCount := window.Reduce(rolling.Count)
if math.IsNaN(requestCount) {
requestCount = 0
}
throughput := window.Reduce(rolling.Count) / windowSize.Seconds()
// TODO: maybe we should wrap this into an helper function of metrics struct
klog.Infof(`{"%s": %f,"%s": %f,"%s": %f}`, metrics.ResponseTime.String(), responseTime, metrics.RequestCount.String(), requestCount, metrics.Throughput.String(), throughput)
_, _ = fmt.Fprintf(res, `{"%s": %f,"%s": %f,"%s": %f}`, metrics.ResponseTime.String(), responseTime, metrics.RequestCount.String(), requestCount, metrics.Throughput.String(), throughput)
}