
本文介绍如何通过 redis.pool 在多个 http 路由中高效、线程安全地共享 redis 连接,避免重复拨号与认证,并给出完整初始化与使用示例。
本文介绍如何通过 redis.pool 在多个 http 路由中高效、线程安全地共享 redis 连接,避免重复拨号与认证,并给出完整初始化与使用示例。
在 Go Web 开发中,若每个 HTTP 处理函数(如 http.HandlerFunc)都独立执行 redis.Dial 和 AUTH 认证,不仅造成连接开销大、性能下降,更会因连接未复用引发资源泄漏或并发竞争问题。Go 的 net.Conn 并非并发安全,直接将单个 redis.Conn 实例全局共享至多个 goroutine(如不同请求协程)将导致不可预测的错误。
正确的做法是使用连接池(*redis.Pool),它由 github.com/garyburd/redigo/redis 提供,能自动管理连接的创建、复用、回收与超时。关键在于:所有连接初始化逻辑(包括网络拨号与认证)应封装在 Pool.Dial 回调中,而非在包级变量声明处执行——因为 Go 不允许在函数外执行语句(如 := 或 c.Do()),仅支持变量声明与初始化常量。
以下为推荐实现方式:
✅ 正确初始化 Redis 连接池(使用 init())
package main
import (
"time"
"github.com/garyburd/redigo/redis"
)
var redisPool *redis.Pool
const (
server = "localhost:6379"
password = "testing"
)
func init() {
redisPool = &redis.Pool{
MaxIdle: 5, // 最大空闲连接数
MaxActive: 20, // 最大活跃连接数(0 表示无限制)
IdleTimeout: 240 * time.Second,
Wait: true, // 获取连接时阻塞等待(推荐开启)
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", server)
if err != nil {
return nil, err
}
// 执行 AUTH 认证;失败则关闭连接并返回错误
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
return c, nil
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := c.Do("PING")
return err
},
}
}⚠️ 注意事项:
- TestOnBorrow 是可选但强烈建议的配置,用于在从池中取出连接前检测其可用性(如是否断连),避免将失效连接交付业务逻辑。
- Wait: true 确保当池中无空闲连接时,Get() 会阻塞等待而非立即返回错误,提升请求成功率。
- MaxActive 应根据服务 QPS 与 Redis 实例负载合理设置,避免压垮 Redis 或耗尽本地文件描述符。
✅ 在路由处理函数中安全使用连接
每个 HTTP 请求应独立获取与归还连接,遵循“Get → Use → Close”模式。conn.Close() 并非真正关闭底层 socket,而是将连接归还至池中复用:
func handleUserCount(w http.ResponseWriter, r *http.Request) {
conn := redisPool.Get()
defer conn.Close() // 归还连接,非销毁
// 检查连接本身是否已出错(如拨号失败、认证失败、网络中断)
if err := conn.Err(); err != nil {
http.Error(w, "Redis connection error: "+err.Error(), http.StatusInternalServerError)
return
}
count, err := redis.Int(conn.Do("INCR", "user:count"))
if err != nil {
http.Error(w, "Redis operation failed: "+err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Current user count: %d", count)
}✅ 总结
- ❌ 禁止在包级作用域执行 redis.Dial 或 c.Do("AUTH", ...) —— 编译不通过且逻辑错误;
- ✅ 使用 redis.Pool 统一管理连接生命周期,Dial 回调内完成认证;
- ✅ 每次请求调用 pool.Get() 获取连接,defer conn.Close() 确保及时归还;
- ✅ 始终检查 conn.Err() 判断连接状态,再执行业务命令;
- ✅ 合理配置 MaxIdle、MaxActive 和 IdleTimeout,兼顾性能与稳定性。
通过该方案,所有路由可共享同一套健壮、并发安全的 Redis 连接池,显著降低延迟、提升吞吐,并消除资源泄漏风险。











