rakyll / hey

HTTP load generator, ApacheBench (ab) replacement
Apache License 2.0
18k stars 1.19k forks source link

Only 1,000,000 request could be sent. #166

Open ibmalan opened 5 years ago

ibmalan commented 5 years ago

When I ran performance with 2 mins or 5 mins duration, only 1,000,000 requests could be sent, Is it the maximum supported requests number? root@hey-client-http:~# hey -z 2m -c 1000 http://10.240.0.10 Summary: Total: 120.0524 secs Slowest: 1.1497 secs Fastest: 0.0007 secs Average: 0.1200 secs Requests/sec: 16104.9422

Response time histogram: 0.001 [1] | 0.116 [964806] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.231 [35028] |■ 0.345 [62] | 0.460 [0] | 0.575 [0] | 0.690 [0] | 0.805 [0] | 0.920 [0] | 1.035 [5] | 1.150 [98] |

Latency distribution: 10% in 0.0195 secs 25% in 0.0378 secs 50% in 0.0672 secs 75% in 0.0858 secs 90% in 0.0918 secs 95% in 0.0980 secs 99% in 0.1383 secs

Details (average, fastest, slowest): DNS+dialup: 0.0001 secs, 0.0007 secs, 1.1497 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0001 secs, 0.0000 secs, 0.0809 secs resp wait: 0.0086 secs, 0.0006 secs, 1.0384 secs resp read: 0.0628 secs, 0.0000 secs, 0.2012 secs

Status code distribution: [200] 1000000 responses

root@hey-client-http:~# hey -z 5m -c 1000 http://10.240.0.10 Summary: Total: 300.0479 secs Slowest: 1.1191 secs Fastest: 0.0007 secs Average: 0.3000 secs Requests/sec: 16262.9643

Response time histogram: 0.001 [1] | 0.113 [966105] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.224 [33690] |■ 0.336 [99] | 0.448 [0] | 0.560 [0] | 0.672 [0] | 0.784 [0] | 0.895 [0] | 1.007 [0] | 1.119 [105] |

Latency distribution: 10% in 0.0315 secs 25% in 0.0382 secs 50% in 0.0689 secs 75% in 0.0857 secs 90% in 0.0911 secs 95% in 0.0965 secs 99% in 0.1307 secs

Details (average, fastest, slowest): DNS+dialup: 0.0001 secs, 0.0007 secs, 1.1191 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0001 secs, 0.0000 secs, 0.1287 secs resp wait: 0.0116 secs, 0.0006 secs, 1.0369 secs resp read: 0.1620 secs, 0.0000 secs, 0.2002 secs

Status code distribution: [200] 1000000 responses

root@hey-client-http:~# hey -n 5000000 -c 5000 http://10.240.0.10 Summary: Total: 1146.3241 secs Slowest: 19.1257 secs Fastest: 0.0008 secs Average: 5.6521 secs Requests/sec: 4361.7681

Response time histogram: 0.001 [1] | 1.913 [868495] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 3.826 [115158] |■■■■■ 5.738 [15139] |■ 7.651 [266] | 9.563 [143] | 11.476 [252] | 13.388 [32] | 15.301 [462] | 17.213 [8] | 19.126 [44] |

Latency distribution: 10% in 0.1668 secs 25% in 0.3729 secs 50% in 0.7270 secs 75% in 1.3417 secs 90% in 2.1572 secs 95% in 2.8843 secs 99% in 4.1318 secs

Details (average, fastest, slowest): DNS+dialup: 0.0015 secs, 0.0008 secs, 19.1257 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0086 secs, 0.0000 secs, 0.7925 secs resp wait: 0.1626 secs, 0.0006 secs, 16.1902 secs resp read: 2.1442 secs, 0.0000 secs, 4.8325 secs

Status code distribution: [200] 1000000 responses

Error distribution: [22] Get http://10.240.0.10: dial tcp 10.240.0.10:80: connect: connection timed out [1315] Get http://10.240.0.10: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

lupguo commented 5 years ago
  1. The maximum setting with resource report is 1 million: const maxRes = 1000000
  2. For more than 1 million requests, hey processes continue to execute in the back end, only statistically, those with more than 1 million status codes are not recorded: if len(r.r.lats) < maxRes {... }
  3. Snapshot captures the snapshot information related to the report
// We report for max 1M results.
const maxRes = 1000000

// Create report obeject
func newReport(w io.Writer, results chan *result, output string, n int) *report {
    cap := min(n, maxRes)
    return &report{
        output:      output,
        results:     results,
        done:        make(chan bool, 1),
        errorDist:   make(map[string]int),
        w:           w,
        connLats:    make([]float64, 0, cap),
        dnsLats:     make([]float64, 0, cap),
        reqLats:     make([]float64, 0, cap),
        resLats:     make([]float64, 0, cap),
        delayLats:   make([]float64, 0, cap),
        lats:        make([]float64, 0, cap),
        statusCodes: make([]int, 0, cap),
    }
}

// Run reporter in a goroutine, watch `if len(r.resLats) < maxRes {...}`
func runReporter(r *report) {
    // Loop will continue until channel is closed
    for res := range r.results {
        r.numRes++
        if res.err != nil {
            r.errorDist[res.err.Error()]++
        } else {
            r.avgTotal += res.duration.Seconds()
            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()
            if len(r.resLats) < maxRes {
                r.lats = append(r.lats, res.duration.Seconds())
                r.connLats = append(r.connLats, res.connDuration.Seconds())
                r.dnsLats = append(r.dnsLats, res.dnsDuration.Seconds())
                r.reqLats = append(r.reqLats, res.reqDuration.Seconds())
                r.delayLats = append(r.delayLats, res.delayDuration.Seconds())
                r.resLats = append(r.resLats, res.resDuration.Seconds())
                r.statusCodes = append(r.statusCodes, res.statusCode)
                r.offsets = append(r.offsets, res.offset.Seconds())
            }
            if res.contentLength > 0 {
                r.sizeTotal += res.contentLength
            }
        }
    }
    // Signal reporter is done.
    r.done <- true
}

// report snapshot
func (r *report) snapshot() Report {
    snapshot := Report{
        ...
        statusCodeDist := make(map[int]int, len(snapshot.StatusCodes))
        for _, statusCode := range snapshot.StatusCodes {
            statusCodeDist[statusCode]++
        }
        snapshot.StatusCodeDist = statusCodeDist
        return snapshot
    }
}

// the template: hey/requester/print.go
Status code distribution:{{ range $code, $num := .StatusCodeDist }}

  [{{ $code }}] {{ $num }} responses{{ end }}
Tronic commented 5 years ago

If it statistically keeps tracking more than a million, could it also print the numbers in a consistent manner in the report (e.g. report the true number of 200 responses)? Benchmarking Python http server frameworks, I cannot run for more than about 20 seconds before hitting the limit, and I need to run longer to gain sufficient accuracy to see if some implementation change made any difference.