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 toDialthat accepts a context.MaxIdle int: The maximum number of idle connections to keep in the pool. IfMaxIdleis zero, no idle connections are kept.MaxActive int: The maximum number of connections that can be allocated from the pool at any time. IfMaxActiveis 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: IftrueandMaxActiveis reached,Get()will block until a connection becomes available. Iffalse,Get()will return anErrPoolExhaustederror 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
},
}