1
1
package main
2
2
3
3
import (
4
- "flag"
5
4
"fmt"
5
+ "io"
6
6
"io/ioutil"
7
7
"os"
8
8
"sync"
@@ -13,20 +13,16 @@ import (
13
13
"github.com/valyala/fasthttp"
14
14
)
15
15
16
- const (
17
- maxRps = 10000000
18
- requestsInterval = 100 * time .Millisecond
19
- )
20
-
21
16
type bombardier struct {
22
17
conf config
23
18
requestHeaders * fasthttp.RequestHeader
24
19
barrier completionBarrier
25
20
26
- bytesWritten int64
27
- timeTaken time.Duration
28
- latencies * stats
29
- requests * stats
21
+ bytesTotal int64
22
+ bytesData int64
23
+ timeTaken time.Duration
24
+ latencies * stats
25
+ requests * stats
30
26
31
27
client * fasthttp.Client
32
28
done chan bool
@@ -46,6 +42,9 @@ type bombardier struct {
46
42
47
43
// Progress bar
48
44
bar * pb.ProgressBar
45
+
46
+ // Output
47
+ out io.Writer
49
48
}
50
49
51
50
func newBombardier (c config ) (* bombardier , error ) {
@@ -65,10 +64,11 @@ func newBombardier(c config) (*bombardier, error) {
65
64
b .bar = pb .New (int (b .conf .duration .Seconds ()))
66
65
b .bar .ShowCounters = false
67
66
b .bar .ShowPercent = false
68
- b .barrier = newTimedCompletionBarrier (int ( c .numConns ) , * c .duration , func () {
67
+ b .barrier = newTimedCompletionBarrier (c .numConns , tickDuration , * c .duration , func () {
69
68
b .bar .Increment ()
70
69
})
71
70
}
71
+ b .out = os .Stdout
72
72
b .client = & fasthttp.Client {
73
73
MaxConnsPerHost : int (c .numConns ),
74
74
}
@@ -89,15 +89,16 @@ func (b *bombardier) prepareRequest() (*fasthttp.Request, *fasthttp.Response) {
89
89
return req , resp
90
90
}
91
91
92
- func (b * bombardier ) fireRequest (req * fasthttp.Request , resp * fasthttp.Response ) (bytesWritten int64 , code int , msTaken uint64 ) {
92
+ func (b * bombardier ) fireRequest (req * fasthttp.Request , resp * fasthttp.Response ) (bytesData , bytesTotal int64 , code int , msTaken uint64 ) {
93
93
start := time .Now ()
94
94
err := b .client .DoTimeout (req , resp , b .conf .timeout )
95
95
if err != nil {
96
96
code = 0
97
97
} else {
98
98
code = resp .StatusCode ()
99
99
}
100
- bytesWritten , _ = resp .WriteTo (ioutil .Discard )
100
+ bytesData = int64 (len (resp .Body ()))
101
+ bytesTotal , _ = resp .WriteTo (ioutil .Discard )
101
102
msTaken = uint64 (time .Since (start ).Nanoseconds () / 1000 )
102
103
return
103
104
}
@@ -107,9 +108,10 @@ func (b *bombardier) releaseRequest(req *fasthttp.Request, resp *fasthttp.Respon
107
108
fasthttp .ReleaseResponse (resp )
108
109
}
109
110
110
- func (b * bombardier ) writeStatistics (bytesWritten int64 , code int , msTaken uint64 ) {
111
+ func (b * bombardier ) writeStatistics (bytesData , bytesTotal int64 , code int , msTaken uint64 ) {
111
112
b .latencies .record (msTaken )
112
- atomic .AddInt64 (& b .bytesWritten , bytesWritten )
113
+ atomic .AddInt64 (& b .bytesTotal , bytesTotal )
114
+ atomic .AddInt64 (& b .bytesData , bytesData )
113
115
b .rpl .Lock ()
114
116
b .reqs ++
115
117
b .rpl .Unlock ()
@@ -133,12 +135,12 @@ func (b *bombardier) writeStatistics(bytesWritten int64, code int, msTaken uint6
133
135
atomic .AddUint64 (counter , 1 )
134
136
}
135
137
136
- func (b * bombardier ) Worker () {
138
+ func (b * bombardier ) worker () {
137
139
for b .barrier .grabWork () {
138
140
req , resp := b .prepareRequest ()
139
- bytesWritten , code , msTaken := b .fireRequest (req , resp )
141
+ bytesData , bytesTotal , code , msTaken := b .fireRequest (req , resp )
140
142
b .releaseRequest (req , resp )
141
- b .writeStatistics (bytesWritten , code , msTaken )
143
+ b .writeStatistics (bytesData , bytesTotal , code , msTaken )
142
144
b .barrier .jobDone ()
143
145
}
144
146
}
@@ -174,94 +176,88 @@ func (b *bombardier) bombard() {
174
176
bombardmentBegin := time .Now ()
175
177
b .start = time .Now ()
176
178
for i := uint64 (0 ); i < b .conf .numConns ; i ++ {
177
- go b .Worker ()
179
+ go b .worker ()
178
180
}
179
181
go b .rateMeter ()
180
182
b .barrier .wait ()
181
183
b .timeTaken = time .Since (bombardmentBegin )
182
184
b .done <- true
183
185
<- b .done
184
- b .bar .FinishPrint ("Done!" )
186
+ b .bar .Finish ()
187
+ fmt .Fprintln (b .out , "Done!" )
185
188
}
186
189
187
190
func (b * bombardier ) throughput () float64 {
188
- return float64 (b .bytesWritten ) / b .timeTaken .Seconds ()
191
+ return float64 (b .bytesTotal ) / b .timeTaken .Seconds ()
189
192
}
190
193
191
194
func (b * bombardier ) printIntro () {
192
195
if b .conf .testType == counted {
193
- fmt .Printf ( "Bombarding %v with %v requests using %v connections\n " ,
196
+ fmt .Fprintf ( b . out , "Bombarding %v with %v requests using %v connections\n " ,
194
197
b .conf .url , * b .conf .numReqs , b .conf .numConns )
195
198
} else if b .conf .testType == timed {
196
- fmt .Printf ( "Bombarding %v for %v using %v connections\n " ,
199
+ fmt .Fprintf ( b . out , "Bombarding %v for %v using %v connections\n " ,
197
200
b .conf .url , * b .conf .duration , b .conf .numConns )
198
201
}
199
202
}
200
203
201
204
func (b * bombardier ) printLatencyStats () {
202
205
percentiles := []float64 {50.0 , 75.0 , 90.0 , 99.0 }
203
- fmt .Println ( " Latency Distribution" )
206
+ fmt .Fprintln ( b . out , " Latency Distribution" )
204
207
for i := 0 ; i < len (percentiles ); i ++ {
205
208
p := percentiles [i ]
206
209
n := b .latencies .percentile (p )
207
- fmt .Printf ( " %2.0f%% %10s" , p , formatUnits (float64 (n ), timeUnitsUs , 2 ))
208
- fmt .Printf ( "\n " )
210
+ fmt .Fprintf ( b . out , " %2.0f%% %10s" , p , formatUnits (float64 (n ), timeUnitsUs , 2 ))
211
+ fmt .Fprintf ( b . out , "\n " )
209
212
}
210
213
}
211
214
212
215
func (b * bombardier ) printStats () {
213
- fmt .Printf ( "%10v %10v %10v %10v\n " , "Statistics" , "Avg" , "Stdev" , "Max" )
214
- fmt .Println ( rpsString (b .requests ))
215
- fmt .Println ( latenciesString (b .latencies ))
216
- if * latencies {
216
+ fmt .Fprintf ( b . out , "%10v %10v %10v %10v\n " , "Statistics" , "Avg" , "Stdev" , "Max" )
217
+ fmt .Fprintln ( b . out , rpsString (b .requests ))
218
+ fmt .Fprintln ( b . out , latenciesString (b .latencies ))
219
+ if b . conf . printLatencies {
217
220
b .printLatencyStats ()
218
221
}
219
- fmt .Println ( " HTTP codes:" )
220
- fmt .Printf ( " 1xx - %v, 2xx - %v, 3xx - %v, 4xx - %v, 5xx - %v\n " ,
222
+ fmt .Fprintln ( b . out , " HTTP codes:" )
223
+ fmt .Fprintf ( b . out , " 1xx - %v, 2xx - %v, 3xx - %v, 4xx - %v, 5xx - %v\n " ,
221
224
b .req1xx , b .req2xx , b .req3xx , b .req4xx , b .req5xx )
222
- fmt .Printf (" errored - %v\n " , b .errored )
223
- fmt .Printf (" %-10v %10v/s\n " , "Throughput:" , formatBinary (b .throughput ()))
225
+ fmt .Fprintf (b .out , " errored - %v\n " , b .errored )
226
+ fmt .Fprintf (b .out , " %-10v %10v/s\n " , "Throughput:" , formatBinary (b .throughput ()))
227
+ }
228
+
229
+ func (b * bombardier ) redirectOutputTo (out io.Writer ) {
230
+ b .bar .Output = out
231
+ b .out = out
232
+ }
233
+
234
+ func (b * bombardier ) disableOutput () {
235
+ b .redirectOutputTo (ioutil .Discard )
236
+ b .bar .NotPrint = true
224
237
}
225
238
226
- var (
227
- numReqs = new ( nullableUint64 )
228
- duration = new ( nullableDuration )
229
- headers = new ( headersList )
230
- numConns = flag . Uint64 ( "c" , 200 , "Maximum number of concurrent connections" )
231
- timeout = flag . Duration ( "timeout" , 2 * time . Second , "Socket/request timeout" )
232
- latencies = flag . Bool ( "latencies" , false , "Print latency statistics" )
233
- method = flag . String ( "m" , "GET" , "Request method" )
234
- body = flag . String ( "data" , "" , "Request body" )
239
+ const (
240
+ maxRps = 10000000
241
+ requestsInterval = 100 * time . Millisecond
242
+ tickDuration = 1 * time . Second
243
+ defaultTimeout = 2 * time . Second
244
+
245
+ programName = "bombardier"
246
+
247
+ EXIT_FAILURE = 1
235
248
)
236
249
237
250
func main () {
238
- flag .Var (headers , "H" , "HTTP headers to use" )
239
- flag .Var (numReqs , "n" , "Number of requests" )
240
- flag .Var (duration , "d" , "Duration of test" )
241
- flag .Parse ()
242
- if flag .NArg () == 0 {
243
- fmt .Println ("No URL supplied" )
244
- flag .Usage ()
245
- os .Exit (1 )
246
- }
247
- if flag .NArg () > 1 {
248
- fmt .Println ("Too many arguments are supplied" )
249
- os .Exit (1 )
251
+ config , err := parser .parse (os .Args )
252
+ if err != nil {
253
+ fmt .Println (err )
254
+ parser .usage (os .Stdout )
255
+ os .Exit (EXIT_FAILURE )
250
256
}
251
- bombardier , err := newBombardier (
252
- config {
253
- numConns : * numConns ,
254
- numReqs : numReqs .val ,
255
- duration : duration .val ,
256
- url : flag .Arg (0 ),
257
- headers : headers ,
258
- timeout : * timeout ,
259
- method : * method ,
260
- body : * body ,
261
- })
257
+ bombardier , err := newBombardier (config )
262
258
if err != nil {
263
259
fmt .Println (err )
264
- os .Exit (1 )
260
+ os .Exit (EXIT_FAILURE )
265
261
}
266
262
bombardier .bombard ()
267
263
bombardier .printStats ()
0 commit comments