redis/options.go

201 lines
4.7 KiB
Go
Raw Normal View History

package redis
import (
"crypto/tls"
"errors"
2017-01-26 17:34:09 +03:00
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
2017-02-18 17:42:34 +03:00
"github.com/go-redis/redis/internal/pool"
)
type Options struct {
// The network type, either tcp or unix.
// Default is tcp.
Network string
// host:port address.
Addr string
// Dialer creates new network connection and has priority over
// Network and Addr options.
Dialer func() (net.Conn, error)
2017-05-25 14:16:39 +03:00
// Hook that is called when new connection is established.
OnConnect func(*Conn) error
2016-08-09 16:32:08 +03:00
// Optional password. Must match the password specified in the
// requirepass server configuration option.
Password string
2016-08-09 16:32:08 +03:00
// Database to be selected after connecting to the server.
DB int
2016-08-09 16:32:08 +03:00
// Maximum number of retries before giving up.
// Default is to not retry failed commands.
MaxRetries int
2017-06-01 17:49:27 +03:00
// Maximum backoff between each retry.
// Default is 512 seconds; -1 disables backoff.
2017-05-25 08:08:44 +03:00
MaxRetryBackoff time.Duration
2016-08-09 16:32:08 +03:00
// Dial timeout for establishing new connections.
2016-03-14 17:51:46 +03:00
// Default is 5 seconds.
DialTimeout time.Duration
2016-08-09 16:32:08 +03:00
// Timeout for socket reads. If reached, commands will fail
// with a timeout instead of blocking.
// Default is 3 seconds.
ReadTimeout time.Duration
2016-08-09 16:32:08 +03:00
// Timeout for socket writes. If reached, commands will fail
// with a timeout instead of blocking.
2017-04-17 14:29:41 +03:00
// Default is ReadTimeout.
WriteTimeout time.Duration
2016-08-09 16:32:08 +03:00
// Maximum number of socket connections.
// Default is 10 connections.
PoolSize int
2016-08-09 16:32:08 +03:00
// Amount of time client waits for connection if all connections
// are busy before returning an error.
// Default is ReadTimeout + 1 second.
PoolTimeout time.Duration
2016-08-09 16:32:08 +03:00
// Amount of time after which client closes idle connections.
// Should be less than server's timeout.
// Default is 5 minutes.
IdleTimeout time.Duration
2016-08-09 16:32:08 +03:00
// Frequency of idle checks.
2016-03-17 19:00:47 +03:00
// Default is 1 minute.
// When minus value is set, then idle check is disabled.
2016-03-17 19:00:47 +03:00
IdleCheckFrequency time.Duration
2016-08-09 16:32:08 +03:00
// Enables read only queries on slave nodes.
ReadOnly bool
2016-11-15 21:27:20 +03:00
// TLS Config to use. When set TLS will be negotiated.
TLSConfig *tls.Config
}
2016-06-05 14:10:30 +03:00
func (opt *Options) init() {
if opt.Network == "" {
2016-06-05 14:10:30 +03:00
opt.Network = "tcp"
}
2016-06-05 14:10:30 +03:00
if opt.Dialer == nil {
opt.Dialer = func() (net.Conn, error) {
2016-11-15 21:27:20 +03:00
conn, err := net.DialTimeout(opt.Network, opt.Addr, opt.DialTimeout)
if opt.TLSConfig == nil || err != nil {
return conn, err
}
t := tls.Client(conn, opt.TLSConfig)
return t, t.Handshake()
2016-06-05 14:10:30 +03:00
}
}
if opt.PoolSize == 0 {
opt.PoolSize = 10
}
if opt.DialTimeout == 0 {
2016-06-05 14:10:30 +03:00
opt.DialTimeout = 5 * time.Second
}
2017-05-25 08:08:44 +03:00
switch opt.ReadTimeout {
case -1:
opt.ReadTimeout = 0
2017-05-25 08:08:44 +03:00
case 0:
opt.ReadTimeout = 3 * time.Second
}
2017-05-25 08:08:44 +03:00
switch opt.WriteTimeout {
case -1:
opt.WriteTimeout = 0
2017-05-25 08:08:44 +03:00
case 0:
opt.WriteTimeout = opt.ReadTimeout
}
if opt.PoolTimeout == 0 {
opt.PoolTimeout = opt.ReadTimeout + time.Second
}
if opt.IdleTimeout == 0 {
opt.IdleTimeout = 5 * time.Minute
}
2016-03-17 19:00:47 +03:00
if opt.IdleCheckFrequency == 0 {
2016-06-05 14:10:30 +03:00
opt.IdleCheckFrequency = time.Minute
2016-03-17 19:00:47 +03:00
}
2017-05-25 08:08:44 +03:00
switch opt.MaxRetryBackoff {
case -1:
opt.MaxRetryBackoff = 0
case 0:
opt.MaxRetryBackoff = 512 * time.Millisecond
}
2016-03-17 19:00:47 +03:00
}
// ParseURL parses a redis URL into options that can be used to connect to redis
func ParseURL(redisURL string) (*Options, error) {
o := &Options{Network: "tcp"}
u, err := url.Parse(redisURL)
if err != nil {
return nil, err
}
if u.Scheme != "redis" && u.Scheme != "rediss" {
return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
}
if u.User != nil {
if p, ok := u.User.Password(); ok {
o.Password = p
}
}
if len(u.Query()) > 0 {
return nil, errors.New("no options supported")
}
h, p, err := net.SplitHostPort(u.Host)
if err != nil {
h = u.Host
}
if h == "" {
h = "localhost"
}
if p == "" {
p = "6379"
}
o.Addr = net.JoinHostPort(h, p)
f := strings.FieldsFunc(u.Path, func(r rune) bool {
return r == '/'
})
switch len(f) {
case 0:
o.DB = 0
case 1:
if o.DB, err = strconv.Atoi(f[0]); err != nil {
2017-01-26 17:34:09 +03:00
return nil, fmt.Errorf("invalid redis database number: %q", f[0])
}
default:
return nil, errors.New("invalid redis URL path: " + u.Path)
}
if u.Scheme == "rediss" {
o.TLSConfig = &tls.Config{ServerName: h}
}
return o, nil
}
func newConnPool(opt *Options) *pool.ConnPool {
return pool.NewConnPool(
2016-06-05 14:10:30 +03:00
opt.Dialer,
opt.PoolSize,
opt.PoolTimeout,
opt.IdleTimeout,
opt.IdleCheckFrequency,
2016-03-17 19:00:47 +03:00
)
}
// PoolStats contains pool state information and accumulated stats.
type PoolStats struct {
Requests uint32 // number of times a connection was requested by the pool
Hits uint32 // number of times free connection was found in the pool
Timeouts uint32 // number of times a wait timeout occurred
TotalConns uint32 // the number of total connections in the pool
FreeConns uint32 // the number of free connections in the pool
}