Publicize vegeta.Result

This commit is contained in:
Tomás Senart
2013-09-09 00:56:38 +01:00
parent b66534db26
commit bab3d9f89a
4 changed files with 54 additions and 54 deletions

View File

@@ -13,31 +13,31 @@ import (
func Attack(targets Targets, rate uint64, duration time.Duration, rep Reporter) {
hits := make(chan *http.Request, rate*uint64((duration).Seconds()))
defer close(hits)
responses := make(chan *result, cap(hits))
defer close(responses)
go drill(rate, hits, responses) // Attack!
results := make(chan *Result, cap(hits))
defer close(results)
go drill(rate, hits, results) // Attack!
for i := 0; i < cap(hits); i++ {
hits <- targets[i%len(targets)]
}
// Wait for all requests to finish
for i := 0; i < cap(responses); i++ {
rep.add(<-responses)
for i := 0; i < cap(results); i++ {
rep.add(<-results)
}
}
// result represents the metrics we want out of an http.Response
type result struct {
code uint64
timestamp time.Time
timing time.Duration
bytesOut uint64
bytesIn uint64
err error
// Result represents the metrics we want out of an http.Response
type Result struct {
Code uint64
Timestamp time.Time
Timing time.Duration
BytesOut uint64
BytesIn uint64
Error error
}
// drill loops over the passed reqs channel and executes each request.
// It is throttled to the rate specified.
func drill(rate uint64, reqs chan *http.Request, res chan *result) {
func drill(rate uint64, reqs chan *http.Request, res chan *Result) {
throttle := time.Tick(time.Duration(1e9 / rate))
for req := range reqs {
<-throttle
@@ -48,19 +48,19 @@ func drill(rate uint64, reqs chan *http.Request, res chan *result) {
// hit executes the passed http.Request and puts a generated *result into res.
// Both transport errors and unsucessfull requests (non {2xx,3xx}) are
// considered errors which are set in the Response.
func hit(req *http.Request, res chan *result) {
func hit(req *http.Request, res chan *Result) {
began := time.Now()
r, err := http.DefaultClient.Do(req)
result := &result{
timestamp: began,
timing: time.Since(began),
bytesOut: uint64(req.ContentLength),
err: err,
result := &Result{
Timestamp: began,
Timing: time.Since(began),
BytesOut: uint64(req.ContentLength),
Error: err,
}
if err == nil {
result.bytesIn, result.code = uint64(r.ContentLength), uint64(r.StatusCode)
if body, err := ioutil.ReadAll(r.Body); err != nil && (result.code < 200 || result.code >= 300) {
result.err = errors.New(string(body))
result.BytesIn, result.Code = uint64(r.ContentLength), uint64(r.StatusCode)
if body, err := ioutil.ReadAll(r.Body); err != nil && (result.Code < 200 || result.Code >= 300) {
result.Error = errors.New(string(body))
}
}

View File

@@ -7,5 +7,5 @@ import (
// Reporter represents any reporter of the results of the test
type Reporter interface {
Report(io.Writer) error
add(res *result)
add(res *Result)
}

View File

@@ -11,18 +11,18 @@ import (
// Metrics incude avg time per request, success ratio,
// total number of request, avg bytes in and avg bytes out
type TextReporter struct {
responses []*result
results []*Result
}
// NewTextReporter initializes a TextReporter with n responses
func NewTextReporter() *TextReporter {
return &TextReporter{responses: make([]*result, 0)}
return &TextReporter{results: make([]*Result, 0)}
}
// Report computes and writes the report to out.
// It returns an error in case of failure.
func (r *TextReporter) Report(out io.Writer) error {
totalRequests := len(r.responses)
totalRequests := len(r.results)
totalTime := time.Duration(0)
totalBytesOut := uint64(0)
totalBytesIn := uint64(0)
@@ -30,16 +30,16 @@ func (r *TextReporter) Report(out io.Writer) error {
histogram := map[uint64]uint64{}
errors := map[string]struct{}{}
for _, res := range r.responses {
histogram[res.code]++
totalTime += res.timing
totalBytesOut += res.bytesOut
totalBytesIn += res.bytesIn
if res.code >= 200 && res.code < 300 {
for _, res := range r.results {
histogram[res.Code]++
totalTime += res.Timing
totalBytesOut += res.BytesOut
totalBytesIn += res.BytesIn
if res.Code >= 200 && res.Code < 300 {
totalSuccess++
}
if res.err != nil {
errors[res.err.Error()] = struct{}{}
if res.Error != nil {
errors[res.Error.Error()] = struct{}{}
}
}
@@ -71,6 +71,6 @@ func (r *TextReporter) Report(out io.Writer) error {
// add adds a response to be used in the report
// Order of arrival is not relevant for this reporter
func (r *TextReporter) add(res *result) {
r.responses = append(r.responses, res)
func (r *TextReporter) add(res *Result) {
r.results = append(r.results, res)
}

View File

@@ -12,36 +12,36 @@ import (
)
type TimingsPlotReporter struct {
responses *list.List
results *list.List
}
// NewTimingsPlotReporter initializes a TimingsPlotReporter
func NewTimingsPlotReporter() *TimingsPlotReporter {
return &TimingsPlotReporter{responses: list.New()}
return &TimingsPlotReporter{results: list.New()}
}
// add inserts response to be used in the report, sorted by timestamp.
func (r *TimingsPlotReporter) add(res *result) {
func (r *TimingsPlotReporter) add(res *Result) {
// Empty list
if r.responses.Len() == 0 {
r.responses.PushFront(res)
if r.results.Len() == 0 {
r.results.PushFront(res)
return
}
// Happened after all others
if last := r.responses.Back().Value.(*result); last.timestamp.Before(res.timestamp) {
r.responses.PushBack(res)
if last := r.results.Back().Value.(*Result); last.Timestamp.Before(res.Timestamp) {
r.results.PushBack(res)
return
}
// Happened before all others
if first := r.responses.Front().Value.(*result); first.timestamp.After(res.timestamp) {
r.responses.PushFront(res)
if first := r.results.Front().Value.(*Result); first.Timestamp.After(res.Timestamp) {
r.results.PushFront(res)
return
}
// O(n) worst case insertion time
for e := r.responses.Front(); e != nil; e = e.Next() {
needle := e.Value.(*result)
if res.timestamp.Before(needle.timestamp) {
r.responses.InsertBefore(res, e)
for e := r.results.Front(); e != nil; e = e.Next() {
needle := e.Value.(*Result)
if res.Timestamp.Before(needle.Timestamp) {
r.results.InsertBefore(res, e)
return
}
}
@@ -53,10 +53,10 @@ func (r *TimingsPlotReporter) Report(out io.Writer) error {
timestamps := make([]time.Time, 0)
timings := make([]time.Duration, 0)
for e := r.responses.Front(); e != nil; e = e.Next() {
r := e.Value.(*result)
timestamps = append(timestamps, r.timestamp)
timings = append(timings, r.timing)
for e := r.results.Front(); e != nil; e = e.Next() {
r := e.Value.(*Result)
timestamps = append(timestamps, r.Timestamp)
timings = append(timings, r.Timing)
}
p, err := plot.New()