options.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package redis
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/url"
  9. "runtime"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/go-redis/redis/internal/pool"
  14. )
  15. // Limiter is the interface of a rate limiter or a circuit breaker.
  16. type Limiter interface {
  17. // Allow returns nil if operation is allowed or an error otherwise.
  18. // If operation is allowed client must ReportResult of the operation
  19. // whether it is a success or a failure.
  20. Allow() error
  21. // ReportResult reports the result of the previously allowed operation.
  22. // nil indicates a success, non-nil error usually indicates a failure.
  23. ReportResult(result error)
  24. }
  25. type Options struct {
  26. // The network type, either tcp or unix.
  27. // Default is tcp.
  28. Network string
  29. // host:port address.
  30. Addr string
  31. // Dialer creates new network connection and has priority over
  32. // Network and Addr options.
  33. Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
  34. // Hook that is called when new connection is established.
  35. OnConnect func(*Conn) error
  36. // Optional password. Must match the password specified in the
  37. // requirepass server configuration option.
  38. Password string
  39. // Database to be selected after connecting to the server.
  40. DB int
  41. // Maximum number of retries before giving up.
  42. // Default is to not retry failed commands.
  43. MaxRetries int
  44. // Minimum backoff between each retry.
  45. // Default is 8 milliseconds; -1 disables backoff.
  46. MinRetryBackoff time.Duration
  47. // Maximum backoff between each retry.
  48. // Default is 512 milliseconds; -1 disables backoff.
  49. MaxRetryBackoff time.Duration
  50. // Dial timeout for establishing new connections.
  51. // Default is 5 seconds.
  52. DialTimeout time.Duration
  53. // Timeout for socket reads. If reached, commands will fail
  54. // with a timeout instead of blocking. Use value -1 for no timeout and 0 for default.
  55. // Default is 3 seconds.
  56. ReadTimeout time.Duration
  57. // Timeout for socket writes. If reached, commands will fail
  58. // with a timeout instead of blocking.
  59. // Default is ReadTimeout.
  60. WriteTimeout time.Duration
  61. // Maximum number of socket connections.
  62. // Default is 10 connections per every CPU as reported by runtime.NumCPU.
  63. PoolSize int
  64. // Minimum number of idle connections which is useful when establishing
  65. // new connection is slow.
  66. MinIdleConns int
  67. // Connection age at which client retires (closes) the connection.
  68. // Default is to not close aged connections.
  69. MaxConnAge time.Duration
  70. // Amount of time client waits for connection if all connections
  71. // are busy before returning an error.
  72. // Default is ReadTimeout + 1 second.
  73. PoolTimeout time.Duration
  74. // Amount of time after which client closes idle connections.
  75. // Should be less than server's timeout.
  76. // Default is 5 minutes. -1 disables idle timeout check.
  77. IdleTimeout time.Duration
  78. // Frequency of idle checks made by idle connections reaper.
  79. // Default is 1 minute. -1 disables idle connections reaper,
  80. // but idle connections are still discarded by the client
  81. // if IdleTimeout is set.
  82. IdleCheckFrequency time.Duration
  83. // Enables read only queries on slave nodes.
  84. readOnly bool
  85. // TLS Config to use. When set TLS will be negotiated.
  86. TLSConfig *tls.Config
  87. // Limiter interface used to implemented circuit breaker or rate limiter.
  88. Limiter Limiter
  89. }
  90. func (opt *Options) init() {
  91. if opt.Addr == "" {
  92. opt.Addr = "localhost:6379"
  93. }
  94. if opt.Network == "" {
  95. if strings.HasPrefix(opt.Addr, "/") {
  96. opt.Network = "unix"
  97. } else {
  98. opt.Network = "tcp"
  99. }
  100. }
  101. if opt.Dialer == nil {
  102. opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
  103. netDialer := &net.Dialer{
  104. Timeout: opt.DialTimeout,
  105. KeepAlive: 5 * time.Minute,
  106. }
  107. if opt.TLSConfig == nil {
  108. return netDialer.DialContext(ctx, network, addr)
  109. }
  110. return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
  111. }
  112. }
  113. if opt.PoolSize == 0 {
  114. opt.PoolSize = 10 * runtime.NumCPU()
  115. }
  116. if opt.DialTimeout == 0 {
  117. opt.DialTimeout = 5 * time.Second
  118. }
  119. switch opt.ReadTimeout {
  120. case -1:
  121. opt.ReadTimeout = 0
  122. case 0:
  123. opt.ReadTimeout = 3 * time.Second
  124. }
  125. switch opt.WriteTimeout {
  126. case -1:
  127. opt.WriteTimeout = 0
  128. case 0:
  129. opt.WriteTimeout = opt.ReadTimeout
  130. }
  131. if opt.PoolTimeout == 0 {
  132. opt.PoolTimeout = opt.ReadTimeout + time.Second
  133. }
  134. if opt.IdleTimeout == 0 {
  135. opt.IdleTimeout = 5 * time.Minute
  136. }
  137. if opt.IdleCheckFrequency == 0 {
  138. opt.IdleCheckFrequency = time.Minute
  139. }
  140. if opt.MaxRetries == -1 {
  141. opt.MaxRetries = 0
  142. }
  143. switch opt.MinRetryBackoff {
  144. case -1:
  145. opt.MinRetryBackoff = 0
  146. case 0:
  147. opt.MinRetryBackoff = 8 * time.Millisecond
  148. }
  149. switch opt.MaxRetryBackoff {
  150. case -1:
  151. opt.MaxRetryBackoff = 0
  152. case 0:
  153. opt.MaxRetryBackoff = 512 * time.Millisecond
  154. }
  155. }
  156. func (opt *Options) clone() *Options {
  157. clone := *opt
  158. return &clone
  159. }
  160. // ParseURL parses an URL into Options that can be used to connect to Redis.
  161. func ParseURL(redisURL string) (*Options, error) {
  162. o := &Options{Network: "tcp"}
  163. u, err := url.Parse(redisURL)
  164. if err != nil {
  165. return nil, err
  166. }
  167. if u.Scheme != "redis" && u.Scheme != "rediss" {
  168. return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
  169. }
  170. if u.User != nil {
  171. if p, ok := u.User.Password(); ok {
  172. o.Password = p
  173. }
  174. }
  175. if len(u.Query()) > 0 {
  176. return nil, errors.New("no options supported")
  177. }
  178. h, p, err := net.SplitHostPort(u.Host)
  179. if err != nil {
  180. h = u.Host
  181. }
  182. if h == "" {
  183. h = "localhost"
  184. }
  185. if p == "" {
  186. p = "6379"
  187. }
  188. o.Addr = net.JoinHostPort(h, p)
  189. f := strings.FieldsFunc(u.Path, func(r rune) bool {
  190. return r == '/'
  191. })
  192. switch len(f) {
  193. case 0:
  194. o.DB = 0
  195. case 1:
  196. if o.DB, err = strconv.Atoi(f[0]); err != nil {
  197. return nil, fmt.Errorf("invalid redis database number: %q", f[0])
  198. }
  199. default:
  200. return nil, errors.New("invalid redis URL path: " + u.Path)
  201. }
  202. if u.Scheme == "rediss" {
  203. o.TLSConfig = &tls.Config{ServerName: h}
  204. }
  205. return o, nil
  206. }
  207. func newConnPool(opt *Options) *pool.ConnPool {
  208. return pool.NewConnPool(&pool.Options{
  209. Dialer: func(ctx context.Context) (net.Conn, error) {
  210. return opt.Dialer(ctx, opt.Network, opt.Addr)
  211. },
  212. PoolSize: opt.PoolSize,
  213. MinIdleConns: opt.MinIdleConns,
  214. MaxConnAge: opt.MaxConnAge,
  215. PoolTimeout: opt.PoolTimeout,
  216. IdleTimeout: opt.IdleTimeout,
  217. IdleCheckFrequency: opt.IdleCheckFrequency,
  218. })
  219. }