96 lines
2.1 KiB
Go
96 lines
2.1 KiB
Go
package load
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Percentiles 是毫秒延迟分位。
|
|
type Percentiles struct {
|
|
P50 float64
|
|
P95 float64
|
|
P99 float64
|
|
N int
|
|
}
|
|
|
|
func calcPercentiles(ms []float64) Percentiles {
|
|
out := Percentiles{N: len(ms)}
|
|
if len(ms) == 0 {
|
|
return out
|
|
}
|
|
cp := append([]float64(nil), ms...)
|
|
sort.Float64s(cp)
|
|
out.P50 = percentileAt(cp, 0.50)
|
|
out.P95 = percentileAt(cp, 0.95)
|
|
out.P99 = percentileAt(cp, 0.99)
|
|
return out
|
|
}
|
|
|
|
func percentileAt(sorted []float64, p float64) float64 {
|
|
n := len(sorted)
|
|
if n == 1 {
|
|
return sorted[0]
|
|
}
|
|
idx := int(math.Ceil(p*float64(n))) - 1
|
|
if idx < 0 {
|
|
idx = 0
|
|
}
|
|
if idx >= n {
|
|
idx = n - 1
|
|
}
|
|
return sorted[idx]
|
|
}
|
|
|
|
// Report 是一次压测输出:提交/送达分位、错误码分布、掉线数。
|
|
type Report struct {
|
|
Connections int
|
|
Alive int
|
|
Disconnects int
|
|
Sends int
|
|
ActualRate float64
|
|
SubmitMs Percentiles
|
|
DeliverMs Percentiles
|
|
Errors map[string]int
|
|
GroupMembers int
|
|
GroupQueueMs float64
|
|
}
|
|
|
|
func (r Report) Format() string {
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "connections: %d alive: %d disconnects: %d\n", r.Connections, r.Alive, r.Disconnects)
|
|
fmt.Fprintf(&b, "sends: %d", r.Sends)
|
|
if r.ActualRate > 0 {
|
|
fmt.Fprintf(&b, " actual_rate: %.2f/s", r.ActualRate)
|
|
}
|
|
b.WriteByte('\n')
|
|
fmt.Fprintf(&b, "submit_ms: n=%d p50=%.1f p95=%.1f p99=%.1f\n", r.SubmitMs.N, r.SubmitMs.P50, r.SubmitMs.P95, r.SubmitMs.P99)
|
|
fmt.Fprintf(&b, "deliver_ms: n=%d p50=%.1f p95=%.1f p99=%.1f\n", r.DeliverMs.N, r.DeliverMs.P50, r.DeliverMs.P95, r.DeliverMs.P99)
|
|
if r.GroupMembers > 0 {
|
|
fmt.Fprintf(&b, "group: members=%d queue_ms=%.1f\n", r.GroupMembers, r.GroupQueueMs)
|
|
}
|
|
if len(r.Errors) == 0 {
|
|
b.WriteString("errors: (none)\n")
|
|
} else {
|
|
b.WriteString("errors:")
|
|
keys := make([]string, 0, len(r.Errors))
|
|
for k := range r.Errors {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, k := range keys {
|
|
fmt.Fprintf(&b, " %s=%d", k, r.Errors[k])
|
|
}
|
|
b.WriteByte('\n')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func addError(m map[string]int, code string) {
|
|
if code == "" {
|
|
code = "error"
|
|
}
|
|
m[code]++
|
|
}
|