- 将Redis连接方式改为连接池模式,提升连接复用效率 - 修复缓存注释错误,统一标识数据库缓存逻辑 - 添加数据检索结果非空验证,避免空指针异常 - 在数据库操作中添加读写锁保护,确保并发安全性 - 实现数据库查询和执行操作的重试机制,增强稳定性 - 更新配置文件中的缓存和数据库设置,优化缓存策略 - 重构README文档,补充框架特性和性能测试数据 - 添加示例路由配置,完善快速入门指南
263 lines
6.3 KiB
Go
263 lines
6.3 KiB
Go
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)")
|
|
}
|
|
}
|