This repository was archived by the owner on May 31, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathload.go
122 lines (109 loc) · 2.66 KB
/
load.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 ab
import (
"io"
"io/ioutil"
"log"
"net/http"
"os"
"sync"
"time"
)
// Load defines the workload
type Load struct {
// The base request, attributes are set from command line arguments, like http method
// for every new request, make a clone of it
BaseRequest *http.Request
// N is the total number of requests
N int
// C is concurrent number
C int
// Q is the QPS of every worker
Q int
// T is the timeout
T int
// TODO: why use channel instead of array
results chan *result
report *report
}
type result struct {
err error
code int
duration time.Duration
}
func (l *Load) work(num int) {
var t <-chan time.Time
// FIXED: this solve the connect: cannot assign requested address problem
tr := &http.Transport{}
client := http.Client{Transport: tr, Timeout: time.Duration(l.T) * time.Second}
// FIXME: this does not reuse connection
// client := http.Client{Timeout: time.Duration(l.T) * time.Second}
if l.Q > 0 {
log.Printf("QPS is %d", l.Q)
t = time.Tick(time.Duration(1e6/l.Q) * time.Microsecond)
}
for i := 0; i < num; i++ {
// TODO: do we need to put initial value into t? I think not
if l.Q > 0 {
<-t
}
r := new(http.Request)
*r = *l.BaseRequest
s := time.Now()
// res, err := client.Do(l.BaseRequest)
res, err := client.Do(r)
// TODO: it seems forgetting this will also cause file descriptor problem
// res.Body.Close()
t := time.Now()
code := 0
if err != nil {
log.Print(err)
} else {
io.Copy(ioutil.Discard, res.Body)
// FIXME: now the request is canceled
res.Body.Close()
}
if res != nil {
code = res.StatusCode
}
result := &result{duration: t.Sub(s), err: err, code: code}
l.results <- result
}
}
// Run block and finish all requests
func (l *Load) Run() {
log.Print("check config")
if l.C > l.N {
log.Fatalf("total number %d is smaller than concurrent number %d", l.N, l.C)
}
requestPerWorker := l.N / l.C
log.Printf("total request %d", l.N)
log.Printf("request per worker %d", requestPerWorker)
log.Printf("worker number %d", l.C)
log.Print("issue a single request to check if server is running")
// make a single request
client := http.Client{Timeout: time.Duration(l.T) * time.Second}
res, err := client.Do(l.BaseRequest)
if err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, res.Body)
res.Body.Close()
l.results = make(chan *result, l.N)
defer close(l.results)
log.Print("start generating load")
var wg sync.WaitGroup
wg.Add(l.C)
for i := 0; i < l.C; i++ {
go func() {
l.work(requestPerWorker)
wg.Done()
}()
}
wg.Wait()
log.Print("load finished")
l.report = newReport(l.results)
l.report.finalize()
}
func (l *Load) PrintReport() {
l.report.print()
}