github.com/gogf/gf@v1.16.9/database/gredis/gredis_conn.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
     8  
     9  import (
    10  	"context"
    11  	"github.com/gomodule/redigo/redis"
    12  	"reflect"
    13  	"time"
    14  
    15  	"github.com/gogf/gf/container/gvar"
    16  	"github.com/gogf/gf/errors/gcode"
    17  	"github.com/gogf/gf/errors/gerror"
    18  	"github.com/gogf/gf/internal/json"
    19  	"github.com/gogf/gf/os/gtime"
    20  	"github.com/gogf/gf/util/gconv"
    21  )
    22  
    23  // Do sends a command to the server and returns the received reply.
    24  // It uses json.Marshal for struct/slice/map type values before committing them to redis.
    25  // The timeout overrides the read timeout set when dialing the connection.
    26  func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) {
    27  	var (
    28  		reflectValue reflect.Value
    29  		reflectKind  reflect.Kind
    30  	)
    31  	for k, v := range args {
    32  		reflectValue = reflect.ValueOf(v)
    33  		reflectKind = reflectValue.Kind()
    34  		if reflectKind == reflect.Ptr {
    35  			reflectValue = reflectValue.Elem()
    36  			reflectKind = reflectValue.Kind()
    37  		}
    38  		switch reflectKind {
    39  		case
    40  			reflect.Struct,
    41  			reflect.Map,
    42  			reflect.Slice,
    43  			reflect.Array:
    44  			// Ignore slice type of: []byte.
    45  			if _, ok := v.([]byte); !ok {
    46  				if args[k], err = json.Marshal(v); err != nil {
    47  					return nil, err
    48  				}
    49  			}
    50  		}
    51  	}
    52  	if timeout > 0 {
    53  		conn, ok := c.Conn.(redis.ConnWithTimeout)
    54  		if !ok {
    55  			return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
    56  		}
    57  		return conn.DoWithTimeout(timeout, commandName, args...)
    58  	}
    59  	timestampMilli1 := gtime.TimestampMilli()
    60  	reply, err = c.Conn.Do(commandName, args...)
    61  	timestampMilli2 := gtime.TimestampMilli()
    62  
    63  	// Tracing.
    64  	c.addTracingItem(&tracingItem{
    65  		err:         err,
    66  		commandName: commandName,
    67  		arguments:   args,
    68  		costMilli:   timestampMilli2 - timestampMilli1,
    69  	})
    70  	return
    71  }
    72  
    73  // Ctx is a channing function which sets the context for next operation.
    74  func (c *Conn) Ctx(ctx context.Context) *Conn {
    75  	c.ctx = ctx
    76  	return c
    77  }
    78  
    79  // Do sends a command to the server and returns the received reply.
    80  // It uses json.Marshal for struct/slice/map type values before committing them to redis.
    81  func (c *Conn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
    82  	return c.do(0, commandName, args...)
    83  }
    84  
    85  // DoWithTimeout sends a command to the server and returns the received reply.
    86  // The timeout overrides the read timeout set when dialing the connection.
    87  func (c *Conn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) {
    88  	return c.do(timeout, commandName, args...)
    89  }
    90  
    91  // DoVar retrieves and returns the result from command as gvar.Var.
    92  func (c *Conn) DoVar(commandName string, args ...interface{}) (*gvar.Var, error) {
    93  	return resultToVar(c.Do(commandName, args...))
    94  }
    95  
    96  // DoVarWithTimeout retrieves and returns the result from command as gvar.Var.
    97  // The timeout overrides the read timeout set when dialing the connection.
    98  func (c *Conn) DoVarWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (*gvar.Var, error) {
    99  	return resultToVar(c.DoWithTimeout(timeout, commandName, args...))
   100  }
   101  
   102  // ReceiveVar receives a single reply as gvar.Var from the Redis server.
   103  func (c *Conn) ReceiveVar() (*gvar.Var, error) {
   104  	return resultToVar(c.Receive())
   105  }
   106  
   107  // ReceiveVarWithTimeout receives a single reply as gvar.Var from the Redis server.
   108  // The timeout overrides the read timeout set when dialing the connection.
   109  func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) {
   110  	conn, ok := c.Conn.(redis.ConnWithTimeout)
   111  	if !ok {
   112  		return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
   113  	}
   114  	return resultToVar(conn.ReceiveWithTimeout(timeout))
   115  }
   116  
   117  // resultToVar converts redis operation result to gvar.Var.
   118  func resultToVar(result interface{}, err error) (*gvar.Var, error) {
   119  	if err == nil {
   120  		if result, ok := result.([]byte); ok {
   121  			return gvar.New(string(result)), err
   122  		}
   123  		// It treats all returned slice as string slice.
   124  		if result, ok := result.([]interface{}); ok {
   125  			return gvar.New(gconv.Strings(result)), err
   126  		}
   127  	}
   128  	return gvar.New(result), err
   129  }