added httptrace
This commit is contained in:
3
hey.go
3
hey.go
@@ -67,6 +67,7 @@ var (
|
||||
|
||||
disableCompression = flag.Bool("disable-compression", false, "")
|
||||
disableKeepAlives = flag.Bool("disable-keepalive", false, "")
|
||||
enableTrace = flag.Bool("http-trace", false, "")
|
||||
proxyAddr = flag.String("x", "", "")
|
||||
)
|
||||
|
||||
@@ -99,6 +100,7 @@ Options:
|
||||
-cpus Number of used cpu cores.
|
||||
(default for current machine is %d cores)
|
||||
-host HTTP Host header.
|
||||
-http-trace Enable http trace detailed info on various events during request (Experimental)
|
||||
`
|
||||
|
||||
func main() {
|
||||
@@ -198,6 +200,7 @@ func main() {
|
||||
H2: *h2,
|
||||
ProxyAddr: proxyURL,
|
||||
Output: *output,
|
||||
EnableTrace: *enableTrace,
|
||||
}).Run()
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,15 @@ type report struct {
|
||||
average float64
|
||||
rps float64
|
||||
|
||||
avgConn float64
|
||||
avgDns float64
|
||||
avgReq float64
|
||||
avgRes float64
|
||||
avgDelay float64
|
||||
|
||||
results chan *result
|
||||
total time.Duration
|
||||
trace bool
|
||||
|
||||
errorDist map[string]int
|
||||
statusCodeDist map[int]int
|
||||
@@ -43,11 +50,12 @@ type report struct {
|
||||
output string
|
||||
}
|
||||
|
||||
func newReport(size int, results chan *result, output string, total time.Duration) *report {
|
||||
func newReport(size int, results chan *result, output string, total time.Duration, trace bool) *report {
|
||||
return &report{
|
||||
output: output,
|
||||
results: results,
|
||||
total: total,
|
||||
trace: trace,
|
||||
statusCodeDist: make(map[int]int),
|
||||
errorDist: make(map[string]int),
|
||||
}
|
||||
@@ -62,6 +70,13 @@ func (r *report) finalize() {
|
||||
} else {
|
||||
r.lats = append(r.lats, res.duration.Seconds())
|
||||
r.avgTotal += res.duration.Seconds()
|
||||
if r.trace {
|
||||
r.avgConn += res.connDuration.Seconds()
|
||||
r.avgDelay += res.delayDuration.Seconds()
|
||||
r.avgDns += res.dnsDuration.Seconds()
|
||||
r.avgReq += res.reqDuration.Seconds()
|
||||
r.avgRes += res.resDuration.Seconds()
|
||||
}
|
||||
r.statusCodeDist[res.statusCode]++
|
||||
if res.contentLength > 0 {
|
||||
r.sizeTotal += res.contentLength
|
||||
@@ -70,6 +85,13 @@ func (r *report) finalize() {
|
||||
default:
|
||||
r.rps = float64(len(r.lats)) / r.total.Seconds()
|
||||
r.average = r.avgTotal / float64(len(r.lats))
|
||||
if r.trace {
|
||||
r.avgConn = r.avgConn / float64(len(r.lats))
|
||||
r.avgDelay = r.avgDelay / float64(len(r.lats))
|
||||
r.avgDns = r.avgDns / float64(len(r.lats))
|
||||
r.avgReq = r.avgReq / float64(len(r.lats))
|
||||
r.avgRes = r.avgRes / float64(len(r.lats))
|
||||
}
|
||||
r.print()
|
||||
return
|
||||
}
|
||||
@@ -97,6 +119,18 @@ func (r *report) print() {
|
||||
fmt.Printf(" Total data:\t%d bytes\n", r.sizeTotal)
|
||||
fmt.Printf(" Size/request:\t%d bytes\n", r.sizeTotal/int64(len(r.lats)))
|
||||
}
|
||||
|
||||
if r.trace {
|
||||
fmt.Printf("\nHttpTrace:\n")
|
||||
fmt.Printf(" DNS+dialup:\t\t%4.4f secs\n", r.avgConn)
|
||||
if r.avgDns > 0 {
|
||||
fmt.Printf(" DNS lookup:\t\t%4.4f secs\n", r.avgDns)
|
||||
}
|
||||
fmt.Printf(" request Write:\t%4.4f secs\n", r.avgReq)
|
||||
fmt.Printf(" response wait:\t%4.4f secs\n", r.avgDelay)
|
||||
fmt.Printf(" response read:\t%4.4f secs\n", r.avgRes)
|
||||
}
|
||||
|
||||
r.printStatusCodes()
|
||||
r.printHistogram()
|
||||
r.printLatencies()
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -34,6 +35,11 @@ type result struct {
|
||||
err error
|
||||
statusCode int
|
||||
duration time.Duration
|
||||
connDuration time.Duration // connection setup(DNS lookup + Dial up) duration
|
||||
dnsDuration time.Duration // dns lookup duration
|
||||
reqDuration time.Duration // request "write" duration
|
||||
resDuration time.Duration // response "read" duration
|
||||
delayDuration time.Duration // delay between response and request
|
||||
contentLength int64
|
||||
}
|
||||
|
||||
@@ -52,6 +58,9 @@ type Work struct {
|
||||
// H2 is an option to make HTTP/2 requests
|
||||
H2 bool
|
||||
|
||||
// EnableTrace is an option to enable httpTrace
|
||||
EnableTrace bool
|
||||
|
||||
// Timeout in seconds.
|
||||
Timeout int
|
||||
|
||||
@@ -83,16 +92,15 @@ func (b *Work) Run() {
|
||||
start := time.Now()
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-c
|
||||
// TODO(jbd): Progress bar should not be finalized.
|
||||
newReport(b.N, b.results, b.Output, time.Now().Sub(start)).finalize()
|
||||
newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
b.runWorkers()
|
||||
newReport(b.N, b.results, b.Output, time.Now().Sub(start)).finalize()
|
||||
newReport(b.N, b.results, b.Output, time.Now().Sub(start), b.EnableTrace).finalize()
|
||||
close(b.results)
|
||||
}
|
||||
|
||||
@@ -100,19 +108,56 @@ func (b *Work) makeRequest(c *http.Client) {
|
||||
s := time.Now()
|
||||
var size int64
|
||||
var code int
|
||||
|
||||
resp, err := c.Do(cloneRequest(b.Request, b.RequestBody))
|
||||
var dnsDuration, connDuration, resDuration, reqDuration, delayTime time.Duration
|
||||
req := cloneRequest(b.Request, b.RequestBody)
|
||||
if b.EnableTrace {
|
||||
trace := &httptrace.ClientTrace{
|
||||
DNSStart: func(info httptrace.DNSStartInfo) {
|
||||
dnsDuration = time.Now().Sub(s)
|
||||
},
|
||||
DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
|
||||
t := time.Now().Sub(s)
|
||||
dnsDuration = time.Duration(t.Nanoseconds() - dnsDuration.Nanoseconds())
|
||||
},
|
||||
GetConn: func(h string) {
|
||||
connDuration = time.Now().Sub(s)
|
||||
},
|
||||
GotConn: func(connInfo httptrace.GotConnInfo) {
|
||||
t := time.Now().Sub(s)
|
||||
reqDuration = t
|
||||
connDuration = time.Duration(t.Nanoseconds() - connDuration.Nanoseconds())
|
||||
},
|
||||
WroteRequest: func(w httptrace.WroteRequestInfo) {
|
||||
t := time.Now().Sub(s)
|
||||
delayTime = t
|
||||
reqDuration = time.Duration(t.Nanoseconds() - reqDuration.Nanoseconds())
|
||||
},
|
||||
GotFirstResponseByte: func() {
|
||||
resDuration = time.Now().Sub(s)
|
||||
delayTime = time.Duration(resDuration.Nanoseconds() - delayTime.Nanoseconds())
|
||||
},
|
||||
}
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
}
|
||||
resp, err := c.Do(req)
|
||||
if err == nil {
|
||||
size = resp.ContentLength
|
||||
code = resp.StatusCode
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
finish := time.Now().Sub(s)
|
||||
resDuration = time.Duration(finish.Nanoseconds() - resDuration.Nanoseconds())
|
||||
b.results <- &result{
|
||||
statusCode: code,
|
||||
duration: time.Now().Sub(s),
|
||||
duration: finish,
|
||||
err: err,
|
||||
contentLength: size,
|
||||
connDuration: connDuration,
|
||||
dnsDuration: dnsDuration,
|
||||
reqDuration: reqDuration,
|
||||
resDuration: resDuration,
|
||||
delayDuration: delayTime,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user