性能优化 #

一、性能优化概述 #

1.1 优化方向 #

方向 说明
连接池 数据库、Redis连接池
中间件 减少不必要的中间件
内存 减少内存分配
并发 合理使用goroutine

1.2 性能指标 #

指标 说明
QPS 每秒请求数
延迟 请求响应时间
内存 内存使用量
CPU CPU使用率

二、连接池优化 #

2.1 数据库连接池 #

go
func initDB() *gorm.DB {
    dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(err)
    }
    
    sqlDB, err := db.DB()
    if err != nil {
        panic(err)
    }
    
    // 连接池配置
    sqlDB.SetMaxIdleConns(10)           // 最大空闲连接数
    sqlDB.SetMaxOpenConns(100)          // 最大打开连接数
    sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间
    sqlDB.SetConnMaxIdleTime(10 * time.Minute) // 连接最大空闲时间
    
    return db
}

2.2 Redis连接池 #

go
func initRedis() *redis.Client {
    return redis.NewClient(&redis.Options{
        Addr:         "localhost:6379",
        Password:     "",
        DB:           0,
        PoolSize:     100,              // 连接池大小
        MinIdleConns: 10,               // 最小空闲连接数
        MaxRetries:   3,                // 最大重试次数
        DialTimeout:  5 * time.Second,  // 连接超时
        ReadTimeout:  3 * time.Second,  // 读取超时
        WriteTimeout: 3 * time.Second,  // 写入超时
        PoolTimeout:  4 * time.Second,  // 连接池超时
    })
}

2.3 HTTP客户端连接池 #

go
var httpClient *http.Client

func initHTTPClient() {
    httpClient = &http.Client{
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
        },
        Timeout: 10 * time.Second,
    }
}

三、中间件优化 #

3.1 减少中间件 #

go
func main() {
    r := gin.New()
    
    // 只使用必要的中间件
    r.Use(gin.Recovery())
    
    // 日志中间件只在需要时使用
    if gin.Mode() != gin.ReleaseMode {
        r.Use(gin.Logger())
    }
    
    r.Run()
}

3.2 条件中间件 #

go
func main() {
    r := gin.New()
    
    // 只对API路由使用中间件
    api := r.Group("/api")
    api.Use(AuthMiddleware(), RateLimitMiddleware())
    {
        api.GET("/users", getUsers)
    }
    
    // 健康检查不需要中间件
    r.GET("/health", healthCheck)
    
    r.Run()
}

3.3 中间件性能 #

go
// 避免在中间件中做耗时操作
func BadMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 不要这样做:每次请求都查询数据库
        var user User
        DB.First(&user, 1)
        
        c.Next()
    }
}

// 使用缓存
func GoodMiddleware() gin.HandlerFunc {
    cache := make(map[string]User)
    var mu sync.RWMutex
    
    return func(c *gin.Context) {
        mu.RLock()
        user, exists := cache["user_1"]
        mu.RUnlock()
        
        if !exists {
            // 缓存不存在时查询
            DB.First(&user, 1)
            mu.Lock()
            cache["user_1"] = user
            mu.Unlock()
        }
        
        c.Next()
    }
}

四、内存优化 #

4.1 对象复用 #

go
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func handler(c *gin.Context) {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()
    
    buf.WriteString("Hello")
    c.String(200, buf.String())
}

4.2 预分配切片 #

go
// 不好的做法
func bad() []int {
    var result []int
    for i := 0; i < 1000; i++ {
        result = append(result, i)
    }
    return result
}

// 好的做法
func good() []int {
    result := make([]int, 0, 1000)
    for i := 0; i < 1000; i++ {
        result = append(result, i)
    }
    return result
}

4.3 字符串拼接 #

go
// 不好的做法
func badConcat(parts []string) string {
    var result string
    for _, part := range parts {
        result += part
    }
    return result
}

// 好的做法
func goodConcat(parts []string) string {
    var builder strings.Builder
    for _, part := range parts {
        builder.WriteString(part)
    }
    return builder.String()
}

五、并发优化 #

5.1 并发处理 #

go
func batchHandler(c *gin.Context) {
    ids := []int{1, 2, 3, 4, 5}
    
    var wg sync.WaitGroup
    results := make([]User, len(ids))
    
    for i, id := range ids {
        wg.Add(1)
        go func(index, id int) {
            defer wg.Done()
            results[index] = getUser(id)
        }(i, id)
    }
    
    wg.Wait()
    
    c.JSON(200, results)
}

5.2 使用channel #

go
func processConcurrently(items []Item) []Result {
    ch := make(chan Result, len(items))
    
    for _, item := range items {
        go func(item Item) {
            ch <- processItem(item)
        }(item)
    }
    
    var results []Result
    for i := 0; i < len(items); i++ {
        results = append(results, <-ch)
    }
    
    return results
}

5.3 限制并发数 #

go
func processWithLimit(items []Item, limit int) []Result {
    sem := make(chan struct{}, limit)
    var wg sync.WaitGroup
    
    results := make([]Result, len(items))
    
    for i, item := range items {
        wg.Add(1)
        go func(index int, item Item) {
            defer wg.Done()
            sem <- struct{}{}
            defer func() { <-sem }()
            
            results[index] = processItem(item)
        }(i, item)
    }
    
    wg.Wait()
    return results
}

六、缓存优化 #

6.1 内存缓存 #

go
var cache = make(map[string]interface{})
var cacheMu sync.RWMutex

func getWithCache(key string, fn func() (interface{}, error)) (interface{}, error) {
    cacheMu.RLock()
    if val, exists := cache[key]; exists {
        cacheMu.RUnlock()
        return val, nil
    }
    cacheMu.RUnlock()
    
    val, err := fn()
    if err != nil {
        return nil, err
    }
    
    cacheMu.Lock()
    cache[key] = val
    cacheMu.Unlock()
    
    return val, nil
}

6.2 Redis缓存 #

go
func getWithRedisCache(key string, ttl time.Duration, fn func() (interface{}, error)) (interface{}, error) {
    // 尝试从Redis获取
    val, err := redis.Get(key).Result()
    if err == nil {
        return val, nil
    }
    
    // 缓存不存在,执行函数
    result, err := fn()
    if err != nil {
        return nil, err
    }
    
    // 存入Redis
    redis.Set(key, result, ttl)
    
    return result, nil
}

七、性能监控 #

7.1 pprof #

go
import _ "net/http/pprof"

func main() {
    r := gin.Default()
    
    // 注册pprof路由
    r.GET("/debug/pprof/*action", func(c *gin.Context) {
        c.Param("action", c.Param("action")[1:])
        http.HandlerFunc(pprof.Index).ServeHTTP(c.Writer, c.Request)
    })
    
    r.Run()
}

7.2 Prometheus #

bash
go get github.com/gin-contrib/prometheus
go
import "github.com/gin-contrib/prometheus"

func main() {
    r := gin.Default()
    
    p := prometheus.NewPrometheus("gin")
    p.Use(r)
    
    r.Run()
}

7.3 自定义指标 #

go
var (
    requestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration in seconds",
            Buckets: []float64{.001, .005, .01, .05, .1, .5, 1, 5},
        },
        []string{"method", "path", "status"},
    )
)

func init() {
    prometheus.MustRegister(requestDuration)
}

func MetricsMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        duration := time.Since(start).Seconds()
        requestDuration.WithLabelValues(
            c.Request.Method,
            c.Request.URL.Path,
            strconv.Itoa(c.Writer.Status()),
        ).Observe(duration)
    }
}

八、性能测试 #

8.1 基准测试 #

go
func BenchmarkHandler(b *testing.B) {
    r := gin.New()
    r.GET("/test", func(c *gin.Context) {
        c.String(200, "ok")
    })
    
    for i := 0; i < b.N; i++ {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/test", nil)
        r.ServeHTTP(w, req)
    }
}

8.2 压力测试 #

bash
# 使用wrk
wrk -t12 -c400 -d30s http://localhost:8080/

# 使用ab
ab -n 10000 -c 100 http://localhost:8080/

九、总结 #

9.1 核心要点 #

要点 说明
连接池 合理配置连接池参数
中间件 减少不必要的中间件
内存 复用对象,预分配
并发 合理使用goroutine

9.2 最佳实践 #

实践 说明
监控 使用Prometheus监控
测试 进行压力测试
优化 根据瓶颈优化
验证 验证优化效果

9.3 下一步 #

现在你已经掌握了性能优化,接下来让我们学习 单元测试,了解Gin的测试方法!

最后更新:2026-03-28