You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

options.go 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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/v8/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. // Options keeps the settings to setup redis connection.
  26. type Options struct {
  27. // The network type, either tcp or unix.
  28. // Default is tcp.
  29. Network string
  30. // host:port address.
  31. Addr string
  32. // Dialer creates new network connection and has priority over
  33. // Network and Addr options.
  34. Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
  35. // Hook that is called when new connection is established.
  36. OnConnect func(ctx context.Context, cn *Conn) error
  37. // Use the specified Username to authenticate the current connection
  38. // with one of the connections defined in the ACL list when connecting
  39. // to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
  40. Username string
  41. // Optional password. Must match the password specified in the
  42. // requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
  43. // or the User Password when connecting to a Redis 6.0 instance, or greater,
  44. // that is using the Redis ACL system.
  45. Password string
  46. // Database to be selected after connecting to the server.
  47. DB int
  48. // Maximum number of retries before giving up.
  49. // Default is 3 retries; -1 (not 0) disables retries.
  50. MaxRetries int
  51. // Minimum backoff between each retry.
  52. // Default is 8 milliseconds; -1 disables backoff.
  53. MinRetryBackoff time.Duration
  54. // Maximum backoff between each retry.
  55. // Default is 512 milliseconds; -1 disables backoff.
  56. MaxRetryBackoff time.Duration
  57. // Dial timeout for establishing new connections.
  58. // Default is 5 seconds.
  59. DialTimeout time.Duration
  60. // Timeout for socket reads. If reached, commands will fail
  61. // with a timeout instead of blocking. Use value -1 for no timeout and 0 for default.
  62. // Default is 3 seconds.
  63. ReadTimeout time.Duration
  64. // Timeout for socket writes. If reached, commands will fail
  65. // with a timeout instead of blocking.
  66. // Default is ReadTimeout.
  67. WriteTimeout time.Duration
  68. // Maximum number of socket connections.
  69. // Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS.
  70. PoolSize int
  71. // Minimum number of idle connections which is useful when establishing
  72. // new connection is slow.
  73. MinIdleConns int
  74. // Connection age at which client retires (closes) the connection.
  75. // Default is to not close aged connections.
  76. MaxConnAge time.Duration
  77. // Amount of time client waits for connection if all connections
  78. // are busy before returning an error.
  79. // Default is ReadTimeout + 1 second.
  80. PoolTimeout time.Duration
  81. // Amount of time after which client closes idle connections.
  82. // Should be less than server's timeout.
  83. // Default is 5 minutes. -1 disables idle timeout check.
  84. IdleTimeout time.Duration
  85. // Frequency of idle checks made by idle connections reaper.
  86. // Default is 1 minute. -1 disables idle connections reaper,
  87. // but idle connections are still discarded by the client
  88. // if IdleTimeout is set.
  89. IdleCheckFrequency time.Duration
  90. // Enables read only queries on slave nodes.
  91. readOnly bool
  92. // TLS Config to use. When set TLS will be negotiated.
  93. TLSConfig *tls.Config
  94. // Limiter interface used to implemented circuit breaker or rate limiter.
  95. Limiter Limiter
  96. }
  97. func (opt *Options) init() {
  98. if opt.Addr == "" {
  99. opt.Addr = "localhost:6379"
  100. }
  101. if opt.Network == "" {
  102. if strings.HasPrefix(opt.Addr, "/") {
  103. opt.Network = "unix"
  104. } else {
  105. opt.Network = "tcp"
  106. }
  107. }
  108. if opt.DialTimeout == 0 {
  109. opt.DialTimeout = 5 * time.Second
  110. }
  111. if opt.Dialer == nil {
  112. opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
  113. netDialer := &net.Dialer{
  114. Timeout: opt.DialTimeout,
  115. KeepAlive: 5 * time.Minute,
  116. }
  117. if opt.TLSConfig == nil {
  118. return netDialer.DialContext(ctx, network, addr)
  119. }
  120. return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
  121. }
  122. }
  123. if opt.PoolSize == 0 {
  124. opt.PoolSize = 10 * runtime.GOMAXPROCS(0)
  125. }
  126. switch opt.ReadTimeout {
  127. case -1:
  128. opt.ReadTimeout = 0
  129. case 0:
  130. opt.ReadTimeout = 3 * time.Second
  131. }
  132. switch opt.WriteTimeout {
  133. case -1:
  134. opt.WriteTimeout = 0
  135. case 0:
  136. opt.WriteTimeout = opt.ReadTimeout
  137. }
  138. if opt.PoolTimeout == 0 {
  139. opt.PoolTimeout = opt.ReadTimeout + time.Second
  140. }
  141. if opt.IdleTimeout == 0 {
  142. opt.IdleTimeout = 5 * time.Minute
  143. }
  144. if opt.IdleCheckFrequency == 0 {
  145. opt.IdleCheckFrequency = time.Minute
  146. }
  147. if opt.MaxRetries == -1 {
  148. opt.MaxRetries = 0
  149. } else if opt.MaxRetries == 0 {
  150. opt.MaxRetries = 3
  151. }
  152. switch opt.MinRetryBackoff {
  153. case -1:
  154. opt.MinRetryBackoff = 0
  155. case 0:
  156. opt.MinRetryBackoff = 8 * time.Millisecond
  157. }
  158. switch opt.MaxRetryBackoff {
  159. case -1:
  160. opt.MaxRetryBackoff = 0
  161. case 0:
  162. opt.MaxRetryBackoff = 512 * time.Millisecond
  163. }
  164. }
  165. func (opt *Options) clone() *Options {
  166. clone := *opt
  167. return &clone
  168. }
  169. // ParseURL parses an URL into Options that can be used to connect to Redis.
  170. // Scheme is required.
  171. // There are two connection types: by tcp socket and by unix socket.
  172. // Tcp connection:
  173. // redis://<user>:<password>@<host>:<port>/<db_number>
  174. // Unix connection:
  175. // unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
  176. func ParseURL(redisURL string) (*Options, error) {
  177. u, err := url.Parse(redisURL)
  178. if err != nil {
  179. return nil, err
  180. }
  181. switch u.Scheme {
  182. case "redis", "rediss":
  183. return setupTCPConn(u)
  184. case "unix":
  185. return setupUnixConn(u)
  186. default:
  187. return nil, fmt.Errorf("redis: invalid URL scheme: %s", u.Scheme)
  188. }
  189. }
  190. func setupTCPConn(u *url.URL) (*Options, error) {
  191. o := &Options{Network: "tcp"}
  192. o.Username, o.Password = getUserPassword(u)
  193. if len(u.Query()) > 0 {
  194. return nil, errors.New("redis: no options supported")
  195. }
  196. h, p, err := net.SplitHostPort(u.Host)
  197. if err != nil {
  198. h = u.Host
  199. }
  200. if h == "" {
  201. h = "localhost"
  202. }
  203. if p == "" {
  204. p = "6379"
  205. }
  206. o.Addr = net.JoinHostPort(h, p)
  207. f := strings.FieldsFunc(u.Path, func(r rune) bool {
  208. return r == '/'
  209. })
  210. switch len(f) {
  211. case 0:
  212. o.DB = 0
  213. case 1:
  214. if o.DB, err = strconv.Atoi(f[0]); err != nil {
  215. return nil, fmt.Errorf("redis: invalid database number: %q", f[0])
  216. }
  217. default:
  218. return nil, fmt.Errorf("redis: invalid URL path: %s", u.Path)
  219. }
  220. if u.Scheme == "rediss" {
  221. o.TLSConfig = &tls.Config{ServerName: h}
  222. }
  223. return o, nil
  224. }
  225. func setupUnixConn(u *url.URL) (*Options, error) {
  226. o := &Options{
  227. Network: "unix",
  228. }
  229. if strings.TrimSpace(u.Path) == "" { // path is required with unix connection
  230. return nil, errors.New("redis: empty unix socket path")
  231. }
  232. o.Addr = u.Path
  233. o.Username, o.Password = getUserPassword(u)
  234. dbStr := u.Query().Get("db")
  235. if dbStr == "" {
  236. return o, nil // if database is not set, connect to 0 db.
  237. }
  238. db, err := strconv.Atoi(dbStr)
  239. if err != nil {
  240. return nil, fmt.Errorf("redis: invalid database number: %w", err)
  241. }
  242. o.DB = db
  243. return o, nil
  244. }
  245. func getUserPassword(u *url.URL) (string, string) {
  246. var user, password string
  247. if u.User != nil {
  248. user = u.User.Username()
  249. if p, ok := u.User.Password(); ok {
  250. password = p
  251. }
  252. }
  253. return user, password
  254. }
  255. func newConnPool(opt *Options) *pool.ConnPool {
  256. return pool.NewConnPool(&pool.Options{
  257. Dialer: func(ctx context.Context) (net.Conn, error) {
  258. return opt.Dialer(ctx, opt.Network, opt.Addr)
  259. },
  260. PoolSize: opt.PoolSize,
  261. MinIdleConns: opt.MinIdleConns,
  262. MaxConnAge: opt.MaxConnAge,
  263. PoolTimeout: opt.PoolTimeout,
  264. IdleTimeout: opt.IdleTimeout,
  265. IdleCheckFrequency: opt.IdleCheckFrequency,
  266. })
  267. }