性能优化

生产环境的性能优化很重要,合理的配置能让应用更稳定高效。

HTTP 服务器配置

package main

import (
    "net/http"
    "time"
    
    "github.com/gin-gonic/gin"
)

func main() {
    gin.SetMode(gin.ReleaseMode)
    
    r := gin.New()
    
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello")
    })
    
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      r,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    
    srv.ListenAndServe()
}

连接池配置

数据库连接池:

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func setupDB() *sql.DB {
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        panic(err)
    }
    
    db.SetMaxOpenConns(100)
    db.SetMaxIdleConns(10)
    db.SetConnMaxLifetime(time.Hour)
    db.SetConnMaxIdleTime(10 * time.Minute)
    
    return db
}

Redis 连接池:

import "github.com/go-redis/redis/v8"

func setupRedis() *redis.Client {
    return redis.NewClient(&redis.Options{
        Addr:         "localhost:6379",
        PoolSize:     100,
        MinIdleConns: 10,
        MaxRetries:   3,
        DialTimeout:  5 * time.Second,
        ReadTimeout:  3 * time.Second,
        WriteTimeout: 3 * time.Second,
        PoolTimeout:  4 * time.Second,
    })
}

中间件优化

跳过不需要的中间件:

func SkipLogger(skipPaths []string) gin.HandlerFunc {
    return func(c *gin.Context) {
        for _, path := range skipPaths {
            if c.Request.URL.Path == path {
                c.Next()
                return
            }
        }
        
        start := time.Now()
        c.Next()
        log.Printf("%s %s - %v", c.Request.Method, c.Request.URL.Path, time.Since(start))
    }
}

r.Use(SkipLogger([]string{"/health", "/metrics"}))

响应压缩

import "github.com/gin-contrib/gzip"

func main() {
    r := gin.Default()
    
    r.Use(gzip.Gzip(gzip.DefaultCompression))
    
    r.GET("/data", func(c *gin.Context) {
        c.JSON(200, largeData)
    })
    
    r.Run(":8080")
}

响应缓存

func CacheMiddleware(duration time.Duration) gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.Method != "GET" {
            c.Next()
            return
        }
        
        key := c.Request.URL.String()
        if cached, found := cache.Get(key); found {
            c.Data(200, "application/json", cached.([]byte))
            c.Abort()
            return
        }
        
        c.Next()
        
        if c.Writer.Status() == 200 {
            cache.Set(key, c.Writer.(*bytesBuffer).Bytes(), duration)
        }
    }
}

减少内存分配

复用对象:

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

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

并发处理

func parallelFetch(c *gin.Context) {
    var wg sync.WaitGroup
    results := make(chan interface{}, 3)
    
    wg.Add(3)
    
    go func() {
        defer wg.Done()
        results <- fetchUser()
    }()
    
    go func() {
        defer wg.Done()
        results <- fetchOrders()
    }()
    
    go func() {
        defer wg.Done()
        results <- fetchProducts()
    }()
    
    go func() {
        wg.Wait()
        close(results)
    }()
    
    var data []interface{}
    for r := range results {
        data = append(data, r)
    }
    
    c.JSON(200, data)
}

超时控制

func withTimeout(c *gin.Context) {
    ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
    defer cancel()
    
    result, err := fetchDataWithContext(ctx)
    if err != nil {
        c.JSON(500, gin.H{"error": "timeout"})
        return
    }
    
    c.JSON(200, result)
}

编译优化

# 减小二进制大小
go build -ldflags="-w -s" -o main .

# 静态编译
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

性能分析

import (
    "net/http/pprof"
)

func main() {
    r := gin.Default()
    
    if gin.Mode() == gin.DebugMode {
        r.GET("/debug/pprof/*profile", gin.WrapH(http.DefaultServeMux))
    }
    
    r.Run(":8080")
}

分析 CPU:

go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30

分析内存:

go tool pprof http://localhost:8080/debug/pprof/heap

压力测试

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

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

小结

性能优化从配置开始:合理的超时、连接池大小、内存复用。使用 pprof 分析性能瓶颈,用压力测试验证优化效果。生产环境记得关闭调试日志,使用 Release 模式。