218 lines
7.0 KiB
Go
218 lines
7.0 KiB
Go
// Package statistics 统计数据
|
||
package statistics
|
||
|
||
import (
|
||
"fmt"
|
||
"sort"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"go_dreamfactory/stress/tools"
|
||
|
||
"golang.org/x/text/language"
|
||
"golang.org/x/text/message"
|
||
|
||
"go_dreamfactory/stress/model"
|
||
)
|
||
|
||
var (
|
||
// 输出统计数据的时间
|
||
exportStatisticsTime = 1 * time.Second
|
||
p = message.NewPrinter(language.English)
|
||
requestTimeList []uint64 // 所有请求响应时间
|
||
)
|
||
|
||
// ReceivingResults 接收结果并处理
|
||
// 统计的时间都是纳秒,显示的时间 都是毫秒
|
||
// concurrent 并发数
|
||
func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sync.WaitGroup) {
|
||
defer func() {
|
||
wg.Done()
|
||
}()
|
||
var stopChan = make(chan bool)
|
||
// 时间
|
||
var (
|
||
processingTime uint64 // 处理总时间
|
||
requestTime uint64 // 请求总时间
|
||
maxTime uint64 // 最大时长
|
||
minTime uint64 // 最小时长
|
||
successNum uint64 // 成功处理数,code为0
|
||
failureNum uint64 // 处理失败数,code不为0 0
|
||
chanIDLen int // 并发数
|
||
chanIDs = make(map[uint64]bool)
|
||
receivedBytes int64
|
||
mutex = sync.RWMutex{}
|
||
)
|
||
statTime := uint64(time.Now().UnixNano())
|
||
// 错误码/错误个数
|
||
var errCode = &sync.Map{}
|
||
// 定时输出一次计算结果
|
||
ticker := time.NewTicker(exportStatisticsTime)
|
||
go func() {
|
||
for {
|
||
select {
|
||
case <-ticker.C:
|
||
endTime := uint64(time.Now().UnixNano())
|
||
mutex.Lock()
|
||
go calculateData(concurrent, processingTime, endTime-statTime, maxTime, minTime, successNum, failureNum,
|
||
chanIDLen, errCode, receivedBytes)
|
||
mutex.Unlock()
|
||
case <-stopChan:
|
||
// 处理完成
|
||
return
|
||
}
|
||
}
|
||
}()
|
||
header()
|
||
for data := range ch {
|
||
mutex.Lock()
|
||
// fmt.Println("处理一条数据", data.ID, data.Time, data.IsSucceed, data.ErrCode)
|
||
processingTime = processingTime + data.Time
|
||
if maxTime <= data.Time {
|
||
maxTime = data.Time
|
||
}
|
||
if minTime == 0 {
|
||
minTime = data.Time
|
||
} else if minTime > data.Time {
|
||
minTime = data.Time
|
||
}
|
||
// 是否请求成功
|
||
if data.IsSucceed == true {
|
||
successNum = successNum + 1
|
||
} else {
|
||
failureNum = failureNum + 1
|
||
}
|
||
// 统计错误码
|
||
if value, ok := errCode.Load(data.ErrCode); ok {
|
||
valueInt, _ := value.(int)
|
||
errCode.Store(data.ErrCode, valueInt+1)
|
||
} else {
|
||
errCode.Store(data.ErrCode, 1)
|
||
}
|
||
receivedBytes += data.ReceivedBytes
|
||
if _, ok := chanIDs[data.ChanID]; !ok {
|
||
chanIDs[data.ChanID] = true
|
||
chanIDLen = len(chanIDs)
|
||
}
|
||
requestTimeList = append(requestTimeList, data.Time)
|
||
mutex.Unlock()
|
||
}
|
||
// 数据全部接受完成,停止定时输出统计数据
|
||
stopChan <- true
|
||
endTime := uint64(time.Now().UnixNano())
|
||
requestTime = endTime - statTime
|
||
calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIDLen, errCode,
|
||
receivedBytes)
|
||
|
||
fmt.Printf("\n\n")
|
||
fmt.Println("************************* 结果 stat ****************************")
|
||
fmt.Println("处理协程数量:", concurrent)
|
||
// fmt.Println("处理协程数量:", concurrent, "程序处理总时长:", fmt.Sprintf("%.3f", float64(processingTime/concurrent)/1e9), "秒")
|
||
fmt.Println("请求总数(并发数*请求数 -c * -n):", successNum+failureNum, "总请求时间:",
|
||
fmt.Sprintf("%.3f", float64(requestTime)/1e9),
|
||
"秒", "successNum:", successNum, "failureNum:", failureNum)
|
||
printTop(requestTimeList)
|
||
fmt.Println("************************* 结果 end ****************************")
|
||
fmt.Printf("\n\n")
|
||
}
|
||
|
||
// printTop 排序后计算 top 90 95 99
|
||
func printTop(requestTimeList []uint64) {
|
||
if requestTimeList == nil {
|
||
return
|
||
}
|
||
all := tools.MyUint64List{}
|
||
all = requestTimeList
|
||
sort.Sort(all)
|
||
fmt.Println("tp90:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.90)]/1e6)))
|
||
fmt.Println("tp95:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.95)]/1e6)))
|
||
fmt.Println("tp99:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.99)]/1e6)))
|
||
}
|
||
|
||
// calculateData 计算数据
|
||
func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64,
|
||
chanIDLen int, errCode *sync.Map, receivedBytes int64) {
|
||
if processingTime == 0 {
|
||
processingTime = 1
|
||
}
|
||
var (
|
||
qps float64
|
||
averageTime float64
|
||
maxTimeFloat float64
|
||
minTimeFloat float64
|
||
requestTimeFloat float64
|
||
)
|
||
// 平均 QPS 成功数*总协程数/总耗时 (每秒)
|
||
if processingTime != 0 {
|
||
qps = float64(successNum*1e9*concurrent) / float64(processingTime)
|
||
}
|
||
// 平均时长 总耗时/总请求数/并发数 纳秒=>毫秒
|
||
if successNum != 0 && concurrent != 0 {
|
||
averageTime = float64(processingTime) / float64(successNum*1e6)
|
||
}
|
||
// 纳秒=>毫秒
|
||
maxTimeFloat = float64(maxTime) / 1e6
|
||
minTimeFloat = float64(minTime) / 1e6
|
||
requestTimeFloat = float64(requestTime) / 1e9
|
||
// 打印的时长都为毫秒
|
||
table(successNum, failureNum, errCode, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat, chanIDLen,
|
||
receivedBytes)
|
||
}
|
||
|
||
// header 打印表头信息
|
||
func header() {
|
||
fmt.Printf("\n\n")
|
||
// 打印的时长都为毫秒 总请数
|
||
fmt.Println("─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────")
|
||
fmt.Println(" 耗时│ 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 状态码")
|
||
fmt.Println("─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────")
|
||
return
|
||
}
|
||
|
||
// table 打印表格
|
||
func table(successNum, failureNum uint64, errCode *sync.Map,
|
||
qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIDLen int, receivedBytes int64) {
|
||
var (
|
||
speed int64
|
||
)
|
||
if requestTimeFloat > 0 {
|
||
speed = int64(float64(receivedBytes) / requestTimeFloat)
|
||
} else {
|
||
speed = 0
|
||
}
|
||
var (
|
||
receivedBytesStr string
|
||
speedStr string
|
||
)
|
||
// 判断获取下载字节长度是否是未知
|
||
if receivedBytes <= 0 {
|
||
receivedBytesStr = ""
|
||
speedStr = ""
|
||
} else {
|
||
receivedBytesStr = p.Sprintf("%d", receivedBytes)
|
||
speedStr = p.Sprintf("%d", speed)
|
||
}
|
||
// 打印的时长都为毫秒
|
||
result := fmt.Sprintf("%4.0fs│%7d│%7d│%7d│%8.2f│%8.2f│%8.2f│%8.2f│%8s│%8s│%v",
|
||
requestTimeFloat, chanIDLen, successNum, failureNum, qps, maxTimeFloat, minTimeFloat, averageTime,
|
||
receivedBytesStr, speedStr,
|
||
printMap(errCode))
|
||
fmt.Println(result)
|
||
return
|
||
}
|
||
|
||
// printMap 输出错误码、次数 节约字符(终端一行字符大小有限)
|
||
func printMap(errCode *sync.Map) (mapStr string) {
|
||
var (
|
||
mapArr []string
|
||
)
|
||
errCode.Range(func(key, value interface{}) bool {
|
||
mapArr = append(mapArr, fmt.Sprintf("%v:%v", key, value))
|
||
return true
|
||
})
|
||
sort.Strings(mapArr)
|
||
mapStr = strings.Join(mapArr, ";")
|
||
return
|
||
}
|