hotime/example/benchmark_test.go

263 lines
6.3 KiB
Go
Raw Normal View History

package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"sync"
"sync/atomic"
"time"
)
// 压测配置
type BenchConfig struct {
URL string // 测试URL
Concurrency int // 并发数
Duration time.Duration // 持续时间
Timeout time.Duration // 请求超时时间
}
// 压测结果
type BenchResult struct {
TotalRequests int64 // 总请求数
SuccessRequests int64 // 成功请求数
FailedRequests int64 // 失败请求数
TotalDuration time.Duration // 总耗时
MinLatency time.Duration // 最小延迟
MaxLatency time.Duration // 最大延迟
AvgLatency time.Duration // 平均延迟
QPS float64 // 每秒请求数
}
func main() {
fmt.Println("==========================================")
fmt.Println(" HoTime 框架性能压力测试")
fmt.Println("==========================================")
fmt.Println()
// 测试配置
configs := []BenchConfig{
{
URL: "http://127.0.0.1:8081/app/test/hello",
Concurrency: 10,
Duration: 10 * time.Second,
Timeout: 5 * time.Second,
},
{
URL: "http://127.0.0.1:8081/app/test/hello",
Concurrency: 50,
Duration: 10 * time.Second,
Timeout: 5 * time.Second,
},
{
URL: "http://127.0.0.1:8081/app/test/hello",
Concurrency: 100,
Duration: 10 * time.Second,
Timeout: 5 * time.Second,
},
{
URL: "http://127.0.0.1:8081/app/test/hello",
Concurrency: 200,
Duration: 10 * time.Second,
Timeout: 5 * time.Second,
},
}
// 先检查服务是否可用
fmt.Println("正在检查服务是否可用...")
if !checkService(configs[0].URL) {
fmt.Println("❌ 服务不可用,请先启动示例应用:")
fmt.Println(" cd example && go run main.go")
return
}
fmt.Println("✅ 服务已就绪")
fmt.Println()
// 执行压测
for i, config := range configs {
fmt.Printf("【测试 %d】并发数: %d, 持续时间: %v\n", i+1, config.Concurrency, config.Duration)
fmt.Println("------------------------------------------")
result := runBenchmark(config)
printResult(result)
fmt.Println()
// 测试间隔,让服务恢复
if i < len(configs)-1 {
fmt.Println("等待 3 秒后开始下一轮测试...")
time.Sleep(3 * time.Second)
fmt.Println()
}
}
fmt.Println("==========================================")
fmt.Println(" 压力测试完成")
fmt.Println("==========================================")
}
// 检查服务是否可用
func checkService(url string) bool {
client := &http.Client{Timeout: 3 * time.Second}
resp, err := client.Get(url)
if err != nil {
return false
}
defer resp.Body.Close()
return resp.StatusCode == 200
}
// 执行压测
func runBenchmark(config BenchConfig) BenchResult {
var (
totalRequests int64
successRequests int64
failedRequests int64
totalLatency int64 // 纳秒
minLatency int64 = int64(time.Hour)
maxLatency int64
mu sync.Mutex
)
// 创建HTTP客户端
client := &http.Client{
Timeout: config.Timeout,
Transport: &http.Transport{
MaxIdleConns: config.Concurrency * 2,
MaxIdleConnsPerHost: config.Concurrency * 2,
IdleConnTimeout: 90 * time.Second,
},
}
// 控制退出
done := make(chan struct{})
var wg sync.WaitGroup
startTime := time.Now()
// 启动并发goroutine
for i := 0; i < config.Concurrency; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-done:
return
default:
reqStart := time.Now()
success := makeRequest(client, config.URL)
latency := time.Since(reqStart).Nanoseconds()
atomic.AddInt64(&totalRequests, 1)
atomic.AddInt64(&totalLatency, latency)
if success {
atomic.AddInt64(&successRequests, 1)
} else {
atomic.AddInt64(&failedRequests, 1)
}
// 更新最小/最大延迟
mu.Lock()
if latency < minLatency {
minLatency = latency
}
if latency > maxLatency {
maxLatency = latency
}
mu.Unlock()
}
}
}()
}
// 等待指定时间
time.Sleep(config.Duration)
close(done)
wg.Wait()
totalDuration := time.Since(startTime)
// 计算结果
result := BenchResult{
TotalRequests: totalRequests,
SuccessRequests: successRequests,
FailedRequests: failedRequests,
TotalDuration: totalDuration,
MinLatency: time.Duration(minLatency),
MaxLatency: time.Duration(maxLatency),
}
if totalRequests > 0 {
result.AvgLatency = time.Duration(totalLatency / totalRequests)
result.QPS = float64(totalRequests) / totalDuration.Seconds()
}
return result
}
// 发起单个请求
func makeRequest(client *http.Client, url string) bool {
resp, err := client.Get(url)
if err != nil {
return false
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return false
}
// 验证响应
if resp.StatusCode != 200 {
return false
}
// 验证JSON格式
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return false
}
// 验证返回状态
if status, ok := result["status"].(float64); !ok || status != 0 {
return false
}
return true
}
// 打印结果
func printResult(result BenchResult) {
successRate := float64(result.SuccessRequests) / float64(result.TotalRequests) * 100
fmt.Printf("总请求数: %d\n", result.TotalRequests)
fmt.Printf("成功请求: %d\n", result.SuccessRequests)
fmt.Printf("失败请求: %d\n", result.FailedRequests)
fmt.Printf("成功率: %.2f%%\n", successRate)
fmt.Printf("总耗时: %v\n", result.TotalDuration.Round(time.Millisecond))
fmt.Printf("QPS: %.2f 请求/秒\n", result.QPS)
fmt.Printf("最小延迟: %v\n", result.MinLatency.Round(time.Microsecond))
fmt.Printf("最大延迟: %v\n", result.MaxLatency.Round(time.Microsecond))
fmt.Printf("平均延迟: %v\n", result.AvgLatency.Round(time.Microsecond))
// 性能评级
fmt.Print("性能评级: ")
switch {
case result.QPS >= 10000:
fmt.Println("🚀 优秀 (QPS >= 10000)")
case result.QPS >= 5000:
fmt.Println("⭐ 良好 (QPS >= 5000)")
case result.QPS >= 2000:
fmt.Println("👍 中等 (QPS >= 2000)")
case result.QPS >= 1000:
fmt.Println("📊 一般 (QPS >= 1000)")
default:
fmt.Println("⚠️ 需优化 (QPS < 1000)")
}
}