-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
145 lines (122 loc) · 3.95 KB
/
main.go
File metadata and controls
145 lines (122 loc) · 3.95 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main
import (
"crypto/tls"
"flag"
"fmt"
"io"
"log"
"net/http"
"sync"
"sync/atomic"
"time"
)
const maxConnections = 10000
func main() {
numClients := flag.Int("c", 1, "Number of clients to create.")
numRequests := flag.Int("n", 1000, "Number of requests to make per client.")
sleep := flag.Duration("s", time.Millisecond, "Time to sleep between requests.")
rps := flag.Int("r", 1000, "Requests per second to attempt to make per client.")
url := flag.String("u", "", "URL to make requests to. If not passed, a local built-in server is created and requests are sent to it.")
method := flag.String("m", http.MethodGet, "HTTP method to use for requests.")
insecure := flag.Bool("i", false, "Allow insecure connections.")
flag.Parse()
totalRequests := *numClients * *numRequests
fmt.Println("Number of clients: ", *numClients)
fmt.Println("Number of requests per client: ", *numRequests)
fmt.Println("Total requests: ", totalRequests)
var tickTime time.Duration
if *rps == 0 {
tickTime = *sleep
} else {
fmt.Println("Requests per second per client: ", *rps)
tickTime = time.Duration(1e9 / *rps)
}
if *url == "" {
fmt.Println("URL flag not passed, using built-in server.")
go server()
*url = "http://localhost:8080"
}
var statusCounts = make(map[int]*atomic.Uint64)
var wg sync.WaitGroup
conn := *numClients * 100
if conn > maxConnections {
conn = maxConnections
}
// shared HTTP transport and client for efficient connection reuse
transport := &http.Transport{
MaxIdleConns: conn,
IdleConnTimeout: 14 * time.Second,
ResponseHeaderTimeout: 14 * time.Second,
DisableKeepAlives: false,
}
if *insecure {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
fmt.Println("Making requests...")
fmt.Println("")
startTime := time.Now()
for range *numClients {
wg.Add(1)
go func() {
defer wg.Done()
client(*url, *method, *numRequests, tickTime, statusCounts, transport)
}()
}
wg.Wait()
finishTime := time.Now()
timeTaken := finishTime.Sub(startTime)
successfulRequests := summariseStatusCounts(statusCounts, totalRequests)
fmt.Println()
fmt.Printf("Success: %d/%d\n", successfulRequests, totalRequests)
fmt.Printf("Time taken: %s\n", timeTaken.String())
fmt.Printf("Successful requests per second: %f\n", float64(successfulRequests)/finishTime.Sub(startTime).Seconds())
fmt.Printf("Total requests per second: %f", float64(totalRequests)/finishTime.Sub(startTime).Seconds())
}
func client(url string, method string, numRequests int, sleep time.Duration, statusCounts map[int]*atomic.Uint64, transport *http.Transport) {
httpClient := &http.Client{
Transport: transport,
// do not follow redirects
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
defer httpClient.CloseIdleConnections()
t := time.NewTicker(sleep)
for i := 0; i < numRequests; i++ {
var resp *http.Response
req, _ := http.NewRequest(method, url, nil)
resp, err := httpClient.Do(req)
if err == nil {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
countResponseStatusCode(resp.StatusCode, statusCounts)
}
<-t.C
}
t.Stop()
}
func countResponseStatusCode(code int, statusCounts map[int]*atomic.Uint64) {
if _, ok := statusCounts[code]; !ok {
statusCounts[code] = &atomic.Uint64{}
}
statusCounts[code].Add(1)
}
func summariseStatusCounts(statusCounts map[int]*atomic.Uint64, totalRequests int) uint64 {
var successfulRequests uint64
fmt.Println("Response counts by status code:")
for code, count := range statusCounts {
loaded := count.Load()
if code == 200 {
successfulRequests = loaded
}
fmt.Printf("\t%d: %d/%d (%.2f%%)\n", code, loaded, totalRequests, float64(loaded)/float64(totalRequests)*100)
}
return successfulRequests
}
func server() {
fmt.Println("Starting server on port 8080")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "")
})
log.Fatal(http.ListenAndServe(":8080", nil))
}