github.com/gogf/gf@v1.16.9/database/gredis/gredis.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 // Package gredis provides convenient client for redis server. 8 // 9 // Redis Client. 10 // 11 // Redis Commands Official: https://redis.io/commands 12 // 13 // Redis Chinese Documentation: http://redisdoc.com/ 14 package gredis 15 16 import ( 17 "context" 18 "fmt" 19 "github.com/gogf/gf/internal/intlog" 20 "time" 21 22 "github.com/gogf/gf/container/gmap" 23 "github.com/gogf/gf/container/gvar" 24 "github.com/gomodule/redigo/redis" 25 ) 26 27 // Redis client. 28 type Redis struct { 29 pool *redis.Pool // Underlying connection pool. 30 group string // Configuration group. 31 config *Config // Configuration. 32 ctx context.Context // Context. 33 } 34 35 // Conn is redis connection. 36 type Conn struct { 37 redis.Conn 38 ctx context.Context 39 redis *Redis 40 } 41 42 // Config is redis configuration. 43 type Config struct { 44 Host string `json:"host"` 45 Port int `json:"port"` 46 Db int `json:"db"` 47 Pass string `json:"pass"` // Password for AUTH. 48 MaxIdle int `json:"maxIdle"` // Maximum number of connections allowed to be idle (default is 10) 49 MaxActive int `json:"maxActive"` // Maximum number of connections limit (default is 0 means no limit). 50 IdleTimeout time.Duration `json:"idleTimeout"` // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) 51 MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) 52 ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout. 53 TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed. 54 TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS. 55 } 56 57 // PoolStats is statistics of redis connection pool. 58 type PoolStats struct { 59 redis.PoolStats 60 } 61 62 const ( 63 defaultPoolIdleTimeout = 10 * time.Second 64 defaultPoolConnTimeout = 10 * time.Second 65 defaultPoolMaxIdle = 10 66 defaultPoolMaxActive = 100 67 defaultPoolMaxLifeTime = 30 * time.Second 68 ) 69 70 var ( 71 // Pool map. 72 pools = gmap.NewStrAnyMap(true) 73 ) 74 75 // New creates a redis client object with given configuration. 76 // Redis client maintains a connection pool automatically. 77 func New(config *Config) *Redis { 78 // The MaxIdle is the most important attribute of the connection pool. 79 // Only if this attribute is set, the created connections from client 80 // can not exceed the limit of the server. 81 if config.MaxIdle == 0 { 82 config.MaxIdle = defaultPoolMaxIdle 83 } 84 // This value SHOULD NOT exceed the connection limit of redis server. 85 if config.MaxActive == 0 { 86 config.MaxActive = defaultPoolMaxActive 87 } 88 if config.IdleTimeout == 0 { 89 config.IdleTimeout = defaultPoolIdleTimeout 90 } 91 if config.ConnectTimeout == 0 { 92 config.ConnectTimeout = defaultPoolConnTimeout 93 } 94 if config.MaxConnLifetime == 0 { 95 config.MaxConnLifetime = defaultPoolMaxLifeTime 96 } 97 return &Redis{ 98 config: config, 99 pool: pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} { 100 return &redis.Pool{ 101 Wait: true, 102 IdleTimeout: config.IdleTimeout, 103 MaxActive: config.MaxActive, 104 MaxIdle: config.MaxIdle, 105 MaxConnLifetime: config.MaxConnLifetime, 106 Dial: func() (redis.Conn, error) { 107 c, err := redis.Dial( 108 "tcp", 109 fmt.Sprintf("%s:%d", config.Host, config.Port), 110 redis.DialConnectTimeout(config.ConnectTimeout), 111 redis.DialUseTLS(config.TLS), 112 redis.DialTLSSkipVerify(config.TLSSkipVerify), 113 ) 114 if err != nil { 115 return nil, err 116 } 117 intlog.Printf(context.TODO(), `open new connection, config:%+v`, config) 118 // AUTH 119 if len(config.Pass) > 0 { 120 if _, err := c.Do("AUTH", config.Pass); err != nil { 121 return nil, err 122 } 123 } 124 // DB 125 if _, err := c.Do("SELECT", config.Db); err != nil { 126 return nil, err 127 } 128 return c, nil 129 }, 130 // After the conn is taken from the connection pool, to test if the connection is available, 131 // If error is returned then it closes the connection object and recreate a new connection. 132 TestOnBorrow: func(c redis.Conn, t time.Time) error { 133 _, err := c.Do("PING") 134 return err 135 }, 136 } 137 }).(*redis.Pool), 138 } 139 } 140 141 // NewFromStr creates a redis client object with given configuration string. 142 // Redis client maintains a connection pool automatically. 143 // The parameter <str> like: 144 // 127.0.0.1:6379,0 145 // 127.0.0.1:6379,0,password 146 func NewFromStr(str string) (*Redis, error) { 147 config, err := ConfigFromStr(str) 148 if err != nil { 149 return nil, err 150 } 151 return New(config), nil 152 } 153 154 // Close closes the redis connection pool, 155 // it will release all connections reserved by this pool. 156 // It is not necessary to call Close manually. 157 func (r *Redis) Close() error { 158 if r.group != "" { 159 // If it is an instance object, 160 // it needs to remove it from the instance Map. 161 instances.Remove(r.group) 162 } 163 pools.Remove(fmt.Sprintf("%v", r.config)) 164 return r.pool.Close() 165 } 166 167 // Clone clones and returns a new Redis object, which is a shallow copy of current one. 168 func (r *Redis) Clone() *Redis { 169 newRedis := New(r.config) 170 *newRedis = *r 171 return newRedis 172 } 173 174 // Ctx is a channing function which sets the context for next operation. 175 func (r *Redis) Ctx(ctx context.Context) *Redis { 176 newRedis := r.Clone() 177 newRedis.ctx = ctx 178 return newRedis 179 } 180 181 // Conn returns a raw underlying connection object, 182 // which expose more methods to communicate with server. 183 // **You should call Close function manually if you do not use this connection any further.** 184 func (r *Redis) Conn() *Conn { 185 return &Conn{ 186 Conn: r.pool.Get(), 187 ctx: r.ctx, 188 redis: r, 189 } 190 } 191 192 // GetConn is alias of Conn, see Conn. 193 // Deprecated, use Conn instead. 194 func (r *Redis) GetConn() *Conn { 195 return r.Conn() 196 } 197 198 // SetMaxIdle sets the maximum number of idle connections in the pool. 199 func (r *Redis) SetMaxIdle(value int) { 200 r.pool.MaxIdle = value 201 } 202 203 // SetMaxActive sets the maximum number of connections allocated by the pool at a given time. 204 // When zero, there is no limit on the number of connections in the pool. 205 // 206 // Note that if the pool is at the MaxActive limit, then all the operations will wait for 207 // a connection to be returned to the pool before returning. 208 func (r *Redis) SetMaxActive(value int) { 209 r.pool.MaxActive = value 210 } 211 212 // SetIdleTimeout sets the IdleTimeout attribute of the connection pool. 213 // It closes connections after remaining idle for this duration. If the value 214 // is zero, then idle connections are not closed. Applications should set 215 // the timeout to a value less than the server's timeout. 216 func (r *Redis) SetIdleTimeout(value time.Duration) { 217 r.pool.IdleTimeout = value 218 } 219 220 // SetMaxConnLifetime sets the MaxConnLifetime attribute of the connection pool. 221 // It closes connections older than this duration. If the value is zero, then 222 // the pool does not close connections based on age. 223 func (r *Redis) SetMaxConnLifetime(value time.Duration) { 224 r.pool.MaxConnLifetime = value 225 } 226 227 // Stats returns pool's statistics. 228 func (r *Redis) Stats() *PoolStats { 229 return &PoolStats{r.pool.Stats()} 230 } 231 232 // Do sends a command to the server and returns the received reply. 233 // Do automatically get a connection from pool, and close it when the reply received. 234 // It does not really "close" the connection, but drops it back to the connection pool. 235 func (r *Redis) Do(commandName string, args ...interface{}) (interface{}, error) { 236 conn := &Conn{ 237 Conn: r.pool.Get(), 238 ctx: r.ctx, 239 redis: r, 240 } 241 defer conn.Close() 242 return conn.Do(commandName, args...) 243 } 244 245 // DoWithTimeout sends a command to the server and returns the received reply. 246 // The timeout overrides the read timeout set when dialing the connection. 247 func (r *Redis) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) { 248 conn := &Conn{ 249 Conn: r.pool.Get(), 250 ctx: r.ctx, 251 redis: r, 252 } 253 defer conn.Close() 254 return conn.DoWithTimeout(timeout, commandName, args...) 255 } 256 257 // DoVar returns value from Do as gvar.Var. 258 func (r *Redis) DoVar(commandName string, args ...interface{}) (*gvar.Var, error) { 259 return resultToVar(r.Do(commandName, args...)) 260 } 261 262 // DoVarWithTimeout returns value from Do as gvar.Var. 263 // The timeout overrides the read timeout set when dialing the connection. 264 func (r *Redis) DoVarWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (*gvar.Var, error) { 265 return resultToVar(r.DoWithTimeout(timeout, commandName, args...)) 266 }