redis/options.go

251 lines
6.4 KiB
Go
Raw Normal View History

package redis
import (
2019-06-04 14:05:29 +03:00
"context"
"crypto/tls"
"errors"
2017-01-26 17:34:09 +03:00
"fmt"
"net"
"net/url"
"runtime"
"strconv"
"strings"
"time"
2020-03-11 17:29:16 +03:00
"github.com/go-redis/redis/v8/internal"
"github.com/go-redis/redis/v8/internal/pool"
)
2018-10-14 10:53:48 +03:00
// Limiter is the interface of a rate limiter or a circuit breaker.
type Limiter interface {
2019-06-04 13:30:47 +03:00
// Allow returns nil if operation is allowed or an error otherwise.
// If operation is allowed client must ReportResult of the operation
// whether it is a success or a failure.
2018-10-14 10:53:48 +03:00
Allow() error
2020-02-02 12:09:27 +03:00
// ReportResult reports the result of the previously allowed operation.
// nil indicates a success, non-nil error usually indicates a failure.
2018-10-14 10:53:48 +03:00
ReportResult(result error)
}
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.
2019-06-04 14:05:29 +03:00
Dialer func(ctx context.Context, network, addr string) (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-07-09 13:10:07 +03:00
// Minimum backoff between each retry.
// Default is 8 milliseconds; -1 disables backoff.
MinRetryBackoff time.Duration
2017-06-01 17:49:27 +03:00
// Maximum backoff between each retry.
2017-07-09 13:10:07 +03:00
// Default is 512 milliseconds; -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. Use value -1 for no timeout and 0 for default.
// 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 per every CPU as reported by runtime.NumCPU.
PoolSize int
2018-05-28 17:27:24 +03:00
// Minimum number of idle connections which is useful when establishing
// new connection is slow.
MinIdleConns int
2018-08-12 10:08:21 +03:00
// Connection age at which client retires (closes) the connection.
// Default is to not close aged connections.
MaxConnAge time.Duration
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.
2018-06-11 13:26:24 +03:00
// Default is 5 minutes. -1 disables idle timeout check.
IdleTimeout time.Duration
2018-06-11 13:26:24 +03:00
// Frequency of idle checks made by idle connections reaper.
// Default is 1 minute. -1 disables idle connections reaper,
2018-05-28 17:27:24 +03:00
// but idle connections are still discarded by the client
// if IdleTimeout is set.
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
2020-02-02 12:09:27 +03:00
// Limiter interface used to implemented circuit breaker or rate limiter.
Limiter Limiter
}
2016-06-05 14:10:30 +03:00
func (opt *Options) init() {
2019-01-13 11:36:38 +03:00
if opt.Addr == "" {
opt.Addr = "localhost:6379"
}
if opt.Network == "" {
if strings.HasPrefix(opt.Addr, "/") {
opt.Network = "unix"
} else {
opt.Network = "tcp"
}
}
2016-06-05 14:10:30 +03:00
if opt.Dialer == nil {
2019-06-04 14:05:29 +03:00
opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
2018-07-24 09:57:02 +03:00
netDialer := &net.Dialer{
Timeout: opt.DialTimeout,
KeepAlive: 5 * time.Minute,
}
if opt.TLSConfig == nil {
return netDialer.DialContext(ctx, network, addr)
2016-11-15 21:27:20 +03:00
}
2019-08-23 14:46:40 +03:00
return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
2016-06-05 14:10:30 +03:00
}
}
if opt.PoolSize == 0 {
opt.PoolSize = 10 * runtime.NumCPU()
}
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-07-09 13:10:07 +03:00
if opt.MaxRetries == -1 {
opt.MaxRetries = 0
}
2017-07-09 13:10:07 +03:00
switch opt.MinRetryBackoff {
case -1:
opt.MinRetryBackoff = 0
case 0:
opt.MinRetryBackoff = 8 * time.Millisecond
}
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
}
2020-02-02 15:59:27 +03:00
func (opt *Options) clone() *Options {
clone := *opt
return &clone
}
2017-06-09 13:55:45 +03:00
// ParseURL parses an 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(&pool.Options{
2019-08-18 17:03:32 +03:00
Dialer: func(ctx context.Context) (net.Conn, error) {
2020-03-11 17:26:42 +03:00
var conn net.Conn
err := internal.WithSpan(ctx, "dialer", func(ctx context.Context) error {
var err error
conn, err = opt.Dialer(ctx, opt.Network, opt.Addr)
return err
})
return conn, err
2019-05-18 14:00:07 +03:00
},
PoolSize: opt.PoolSize,
2018-05-28 17:27:24 +03:00
MinIdleConns: opt.MinIdleConns,
2018-08-12 10:08:21 +03:00
MaxConnAge: opt.MaxConnAge,
PoolTimeout: opt.PoolTimeout,
IdleTimeout: opt.IdleTimeout,
IdleCheckFrequency: opt.IdleCheckFrequency,
})
}