Connection Pooling
For concurrent applications like web servers, managing individual Redis connections can be inefficient and error-prone. A connection pool provides a managed set of shared connections, improving performance and reliability.
Redigo's redis.Pool
is a thread-safe connection pool that manages a collection of redis.Conn
instances.
Creating a Pool
The recommended way to create a pool is to initialize a redis.Pool
struct.
func newPool(addr string) *redis.Pool {
return &redis.Pool{
MaxIdle: 3, // Maximum number of idle connections in the pool.
MaxActive: 0, // Maximum number of connections allocated by the pool at a given time. When zero, there is no limit.
IdleTimeout: 240 * time.Second, // Close connections after remaining idle for this duration.
Dial: func () (redis.Conn, error) {
return redis.Dial("tcp", addr)
},
}
}
var pool = newPool("localhost:6379")
Pool Configuration
The redis.Pool
struct has several fields to control its behavior:
Dial func() (redis.Conn, error)
: (Required) A function that creates and returns a new connection.DialContext func(context.Context) (redis.Conn, error)
: An alternative toDial
that accepts a context.MaxIdle int
: The maximum number of idle connections to keep in the pool. IfMaxIdle
is zero, no idle connections are kept.MaxActive int
: The maximum number of connections that can be allocated from the pool at any time. IfMaxActive
is zero, there is no limit.IdleTimeout time.Duration
: If a connection has been idle for longer than this duration, it will be closed. If zero, idle connections are not closed. This should be set to a value less than the Redis server's timeout.Wait bool
: Iftrue
andMaxActive
is reached,Get()
will block until a connection becomes available. Iffalse
,Get()
will return anErrPoolExhausted
error immediately.MaxConnLifetime time.Duration
: Connections older than this duration will be closed, even if they are active.TestOnBorrow func(c redis.Conn, t time.Time) error
: An optional function to check the health of a connection before it's returned to the application. If the function returns an error, the connection is closed.
Getting and Using a Connection
To get a connection from the pool, call the Get()
method. It's crucial to Close()
the connection when you're done with it. Calling Close()
on a pooled connection returns it to the pool, it does not close the underlying network connection.
The standard pattern is to use defer
:
func handleRequest(w http.ResponseWriter, r *http.Request) {
conn := pool.Get()
// Use defer to ensure the connection is returned to the pool.
defer conn.Close()
// Use the connection to execute commands.
reply, err := conn.Do("GET", "my-key")
if err != nil {
// handle error
return
}
// ... process reply
}
For context-aware operations, you can use pool.GetContext(ctx)
.
Health Checks
The TestOnBorrow
function is useful for preventing the use of stale or broken connections. For example, you can PING
the server for connections that have been idle for a while.
pool := &redis.Pool{
// ... other configuration
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := c.Do("PING")
return err
},
}