
本文介绍如何通过 redigo 的 redis.Pool 在多个 http 路由间高效、线程安全地共享 Redis 连接,避免重复拨号与认证,并提供完整初始化与使用示例。
本文介绍如何通过 `redigo` 的 `redis.pool` 在多个 http 路由间高效、线程安全地共享 redis 连接,避免重复拨号与认证,并提供完整初始化与使用示例。
在 Go Web 开发中,若每个 HTTP 处理函数(如 http.HandleFunc 或 gin/Chi 路由)都独立调用 redis.Dial 并执行 AUTH,不仅造成连接开销大、资源浪费,更会因连接非并发安全而引发竞态或连接泄漏。Go 不允许在包级作用域执行函数调用(如 redis.Dial),因此直接在 var 块中初始化连接会编译失败;同时,单个 redis.Conn 实例不可被多个 goroutine 同时使用——这是初学者常见的误区。
正确的做法是使用连接池(redis.Pool),它能自动管理连接的创建、复用、回收与超时释放。redigo 提供的 Pool 是线程安全的,可被任意数量的路由处理器并发获取和归还连接。
✅ 推荐方案:全局 Pool + 初始化认证
将连接池声明为包级变量,并在 init() 函数中完成配置。关键点在于:将 AUTH 步骤内联到 Dial 回调中,确保每次从池中获取的连接均已通过认证且就绪可用:
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, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", server) if err != nil { return nil, err } // 自动完成认证,失败则关闭连接并返回错误 if _, err := c.Do("AUTH", password); err != nil { c.Close() return nil, err } return c, nil }, // 可选:测试连接有效性(如 PING) TestOnBorrow: func(c redis.Conn, t time.Time) error { if time.Since(t) < time.Minute { return nil } _, err := c.Do("PING") return err }, } }
✅ 在路由中安全使用连接
每个请求只需调用 redisPool.Get() 获取连接,使用完毕后务必调用 conn.Close() —— 注意:这不是真正关闭 TCP 连接,而是将其归还至连接池以供复用。建议配合 defer 确保归还:
func handleUserGet(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 } // 执行业务操作,例如 GET user:123 reply, err := redis.String(conn.Do("GET", "user:123")) if err != nil && err != redis.ErrNil { http.Error(w, "Redis GET failed: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"data": reply}) }
⚠️ 注意事项与最佳实践
- 不要缓存 redis.Conn 实例:每个请求必须独立 Get() 和 Close(),禁止跨 goroutine 复用同一连接。
- 合理设置池参数:MaxIdle 过小会导致频繁重建连接;过大则占用过多内存。建议根据 QPS 和平均响应时间压测调整。
- 启用 TestOnBorrow:对长时间空闲的连接执行 PING,可提前发现断连,提升健壮性(轻微性能开销,推荐生产启用)。
- 错误处理不可省略:始终检查 conn.Err()(连接层错误)和 Do() 返回的具体命令错误,二者语义不同。
- 密码安全:生产环境应通过环境变量或配置中心加载 password,避免硬编码。
通过以上方式,你不仅能彻底消除重复 Dial 和 AUTH 的冗余代码,还能获得高性能、高可靠、符合 Go 并发模型的 Redis 访问能力。所有路由共享同一池实例,零额外同步成本,即插即用。