|
- package redis
-
- import (
- "crypto/tls"
- "errors"
- "fmt"
- "net"
- "net/url"
- "runtime"
- "strconv"
- "strings"
- "time"
-
- "github.com/go-redis/redis/internal/pool"
- )
-
- // Limiter is the interface of a rate limiter or a circuit breaker.
- type Limiter interface {
- // Allow returns a nil if operation is allowed or an error otherwise.
- // If operation is allowed client must report the result of operation
- // whether is a success or a failure.
- Allow() error
- // ReportResult reports the result of previously allowed operation.
- // nil indicates a success, non-nil error indicates a failure.
- 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.
- Dialer func() (net.Conn, error)
-
- // Hook that is called when new connection is established.
- OnConnect func(*Conn) error
-
- // Optional password. Must match the password specified in the
- // requirepass server configuration option.
- Password string
- // Database to be selected after connecting to the server.
- DB int
-
- // Maximum number of retries before giving up.
- // Default is to not retry failed commands.
- MaxRetries int
- // Minimum backoff between each retry.
- // Default is 8 milliseconds; -1 disables backoff.
- MinRetryBackoff time.Duration
- // Maximum backoff between each retry.
- // Default is 512 milliseconds; -1 disables backoff.
- MaxRetryBackoff time.Duration
-
- // Dial timeout for establishing new connections.
- // Default is 5 seconds.
- DialTimeout time.Duration
- // 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
- // Timeout for socket writes. If reached, commands will fail
- // with a timeout instead of blocking.
- // Default is ReadTimeout.
- WriteTimeout time.Duration
-
- // Maximum number of socket connections.
- // Default is 10 connections per every CPU as reported by runtime.NumCPU.
- PoolSize int
- // Minimum number of idle connections which is useful when establishing
- // new connection is slow.
- MinIdleConns int
- // Connection age at which client retires (closes) the connection.
- // Default is to not close aged connections.
- MaxConnAge time.Duration
- // Amount of time client waits for connection if all connections
- // are busy before returning an error.
- // Default is ReadTimeout + 1 second.
- PoolTimeout time.Duration
- // Amount of time after which client closes idle connections.
- // Should be less than server's timeout.
- // Default is 5 minutes. -1 disables idle timeout check.
- IdleTimeout time.Duration
- // Frequency of idle checks made by idle connections reaper.
- // Default is 1 minute. -1 disables idle connections reaper,
- // but idle connections are still discarded by the client
- // if IdleTimeout is set.
- IdleCheckFrequency time.Duration
-
- // Enables read only queries on slave nodes.
- readOnly bool
-
- // TLS Config to use. When set TLS will be negotiated.
- TLSConfig *tls.Config
- }
-
- func (opt *Options) init() {
- if opt.Network == "" {
- opt.Network = "tcp"
- }
- if opt.Addr == "" {
- opt.Addr = "localhost:6379"
- }
- if opt.Dialer == nil {
- opt.Dialer = func() (net.Conn, error) {
- netDialer := &net.Dialer{
- Timeout: opt.DialTimeout,
- KeepAlive: 5 * time.Minute,
- }
- if opt.TLSConfig == nil {
- return netDialer.Dial(opt.Network, opt.Addr)
- } else {
- return tls.DialWithDialer(netDialer, opt.Network, opt.Addr, opt.TLSConfig)
- }
- }
- }
- if opt.PoolSize == 0 {
- opt.PoolSize = 10 * runtime.NumCPU()
- }
- if opt.DialTimeout == 0 {
- opt.DialTimeout = 5 * time.Second
- }
- switch opt.ReadTimeout {
- case -1:
- opt.ReadTimeout = 0
- case 0:
- opt.ReadTimeout = 3 * time.Second
- }
- switch opt.WriteTimeout {
- case -1:
- opt.WriteTimeout = 0
- 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
- }
- if opt.IdleCheckFrequency == 0 {
- opt.IdleCheckFrequency = time.Minute
- }
-
- switch opt.MinRetryBackoff {
- case -1:
- opt.MinRetryBackoff = 0
- case 0:
- opt.MinRetryBackoff = 8 * time.Millisecond
- }
- switch opt.MaxRetryBackoff {
- case -1:
- opt.MaxRetryBackoff = 0
- case 0:
- opt.MaxRetryBackoff = 512 * time.Millisecond
- }
- }
-
- // 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 {
- 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{
- Dialer: opt.Dialer,
- PoolSize: opt.PoolSize,
- MinIdleConns: opt.MinIdleConns,
- MaxConnAge: opt.MaxConnAge,
- PoolTimeout: opt.PoolTimeout,
- IdleTimeout: opt.IdleTimeout,
- IdleCheckFrequency: opt.IdleCheckFrequency,
- })
- }
|