added httptrace

This commit is contained in:
Ronak Jain
2016-09-04 21:16:33 +05:30
parent 5d3e4bd56c
commit 2b14fc447d
3 changed files with 89 additions and 7 deletions

3
hey.go
View File

@@ -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()
}

View File

@@ -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()

View File

@@ -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,
}
}