...
1 package redis
2
3 import (
4 "crypto/tls"
5 "errors"
6 "fmt"
7 "net"
8 "net/url"
9 "runtime"
10 "strconv"
11 "strings"
12 "time"
13
14 "github.com/go-redis/redis/internal/pool"
15 )
16
17
18 type Limiter interface {
19
20
21
22 Allow() error
23
24
25 ReportResult(result error)
26 }
27
28 type Options struct {
29
30
31 Network string
32
33 Addr string
34
35
36
37 Dialer func() (net.Conn, error)
38
39
40 OnConnect func(*Conn) error
41
42
43
44 Password string
45
46 DB int
47
48
49
50 MaxRetries int
51
52
53 MinRetryBackoff time.Duration
54
55
56 MaxRetryBackoff time.Duration
57
58
59
60 DialTimeout time.Duration
61
62
63
64 ReadTimeout time.Duration
65
66
67
68 WriteTimeout time.Duration
69
70
71
72 PoolSize int
73
74
75 MinIdleConns int
76
77
78 MaxConnAge time.Duration
79
80
81
82 PoolTimeout time.Duration
83
84
85
86 IdleTimeout time.Duration
87
88
89
90
91 IdleCheckFrequency time.Duration
92
93
94 readOnly bool
95
96
97 TLSConfig *tls.Config
98 }
99
100 func (opt *Options) init() {
101 if opt.Network == "" {
102 opt.Network = "tcp"
103 }
104 if opt.Addr == "" {
105 opt.Addr = "localhost:6379"
106 }
107 if opt.Dialer == nil {
108 opt.Dialer = func() (net.Conn, error) {
109 netDialer := &net.Dialer{
110 Timeout: opt.DialTimeout,
111 KeepAlive: 5 * time.Minute,
112 }
113 if opt.TLSConfig == nil {
114 return netDialer.Dial(opt.Network, opt.Addr)
115 } else {
116 return tls.DialWithDialer(netDialer, opt.Network, opt.Addr, opt.TLSConfig)
117 }
118 }
119 }
120 if opt.PoolSize == 0 {
121 opt.PoolSize = 10 * runtime.NumCPU()
122 }
123 if opt.DialTimeout == 0 {
124 opt.DialTimeout = 5 * time.Second
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
148 switch opt.MinRetryBackoff {
149 case -1:
150 opt.MinRetryBackoff = 0
151 case 0:
152 opt.MinRetryBackoff = 8 * time.Millisecond
153 }
154 switch opt.MaxRetryBackoff {
155 case -1:
156 opt.MaxRetryBackoff = 0
157 case 0:
158 opt.MaxRetryBackoff = 512 * time.Millisecond
159 }
160 }
161
162
163 func ParseURL(redisURL string) (*Options, error) {
164 o := &Options{Network: "tcp"}
165 u, err := url.Parse(redisURL)
166 if err != nil {
167 return nil, err
168 }
169
170 if u.Scheme != "redis" && u.Scheme != "rediss" {
171 return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
172 }
173
174 if u.User != nil {
175 if p, ok := u.User.Password(); ok {
176 o.Password = p
177 }
178 }
179
180 if len(u.Query()) > 0 {
181 return nil, errors.New("no options supported")
182 }
183
184 h, p, err := net.SplitHostPort(u.Host)
185 if err != nil {
186 h = u.Host
187 }
188 if h == "" {
189 h = "localhost"
190 }
191 if p == "" {
192 p = "6379"
193 }
194 o.Addr = net.JoinHostPort(h, p)
195
196 f := strings.FieldsFunc(u.Path, func(r rune) bool {
197 return r == '/'
198 })
199 switch len(f) {
200 case 0:
201 o.DB = 0
202 case 1:
203 if o.DB, err = strconv.Atoi(f[0]); err != nil {
204 return nil, fmt.Errorf("invalid redis database number: %q", f[0])
205 }
206 default:
207 return nil, errors.New("invalid redis URL path: " + u.Path)
208 }
209
210 if u.Scheme == "rediss" {
211 o.TLSConfig = &tls.Config{ServerName: h}
212 }
213 return o, nil
214 }
215
216 func newConnPool(opt *Options) *pool.ConnPool {
217 return pool.NewConnPool(&pool.Options{
218 Dialer: opt.Dialer,
219 PoolSize: opt.PoolSize,
220 MinIdleConns: opt.MinIdleConns,
221 MaxConnAge: opt.MaxConnAge,
222 PoolTimeout: opt.PoolTimeout,
223 IdleTimeout: opt.IdleTimeout,
224 IdleCheckFrequency: opt.IdleCheckFrequency,
225 })
226 }
227
View as plain text