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  }