package cache import ( "errors" "log" "strings" "time" redigo "github.com/gomodule/redigo/redis" ) type RedisPool struct { *redigo.Pool } func NewRedisPool(cfg *Config) *RedisPool { return &RedisPool{&redigo.Pool{ MaxIdle: cfg.MaxIdle, IdleTimeout: cfg.IdleTimeout, MaxActive: cfg.MaxActive, Wait: cfg.Wait, Dial: func() (redigo.Conn, error) { c, err := redigo.Dial("tcp", cfg.Server) if err != nil { log.Println("Redis Dial failed: ", err) return nil, err } if cfg.Password != "" { if _, err := c.Do("AUTH", cfg.Password); err != nil { c.Close() log.Println("Redis AUTH failed: ", err) return nil, err } } return c, err }, TestOnBorrow: func(c redigo.Conn, t time.Time) error { _, err := c.Do("PING") if err != nil { log.Println("Unable to ping to redis server:", err) } return err }, }} } func (p *RedisPool) Do(cmd string, args ...interface{}) (reply interface{}, err error) { conn := pool.Get() defer conn.Close() return conn.Do(cmd, args...) } func (p *RedisPool) GetPool() *redigo.Pool { return pool } func (p *RedisPool) ParseKey(key string, vars []string) (string, error) { arr := strings.Split(key, conf.KeyPlaceholder) actualKey := "" if len(arr) != len(vars)+1 { return "", errors.New("redis/connection.go: Insufficient arguments to parse key") } else { for index, val := range arr { if index == 0 { actualKey = arr[index] } else { actualKey += vars[index-1] + val } } } return getPrefixedKey(actualKey), nil } func (p *RedisPool) getPrefixedKey(key string) string { return conf.KeyPrefix + conf.KeyDelimiter + key } func (p *RedisPool) StripEnvKey(key string) string { return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) } func (p *RedisPool) SplitKey(key string) []string { return strings.Split(key, conf.KeyDelimiter) } func (p *RedisPool) Expire(key string, ttl int) (interface{}, error) { return Do("EXPIRE", key, ttl) } func (p *RedisPool) Persist(key string) (interface{}, error) { return Do("PERSIST", key) } func (p *RedisPool) Del(key string) (interface{}, error) { return Do("DEL", key) } func (p *RedisPool) Set(key string, data interface{}) (interface{}, error) { // set return Do("SET", key, data) } func (p *RedisPool) SetNX(key string, data interface{}) (interface{}, error) { return Do("SETNX", key, data) } func (p *RedisPool) SetEx(key string, data interface{}, ttl int) (interface{}, error) { return Do("SETEX", key, ttl, data) } func (p *RedisPool) Get(key string) (interface{}, error) { // get return Do("GET", key) } func (p *RedisPool) GetStringMap(key string) (map[string]string, error) { // get return redigo.StringMap(Do("GET", key)) } func (p *RedisPool) GetTTL(key string) (time.Duration, error) { ttl, err := redigo.Int64(Do("TTL", key)) return time.Duration(ttl) * time.Second, err } func (p *RedisPool) GetBytes(key string) ([]byte, error) { return redigo.Bytes(Do("GET", key)) } func (p *RedisPool) GetString(key string) (string, error) { return redigo.String(Do("GET", key)) } func (p *RedisPool) GetInt(key string) (int, error) { return redigo.Int(Do("GET", key)) } func (p *RedisPool) GetStringLength(key string) (int, error) { return redigo.Int(Do("STRLEN", key)) } func (p *RedisPool) ZAdd(key string, score float64, data interface{}) (interface{}, error) { return Do("ZADD", key, score, data) } func (p *RedisPool) ZRem(key string, data interface{}) (interface{}, error) { return Do("ZREM", key, data) } func (p *RedisPool) ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { if withScores { return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) } return redigo.Values(Do("ZRANGE", key, start, end)) } func (p *RedisPool) SAdd(setName string, data interface{}) (interface{}, error) { return Do("SADD", setName, data) } func (p *RedisPool) SCard(setName string) (int64, error) { return redigo.Int64(Do("SCARD", setName)) } func (p *RedisPool) SIsMember(setName string, data interface{}) (bool, error) { return redigo.Bool(Do("SISMEMBER", setName, data)) } func (p *RedisPool) SMembers(setName string) ([]string, error) { return redigo.Strings(Do("SMEMBERS", setName)) } func (p *RedisPool) SRem(setName string, data interface{}) (interface{}, error) { return Do("SREM", setName, data) } func (p *RedisPool) HSet(key string, HKey string, data interface{}) (interface{}, error) { return Do("HSET", key, HKey, data) } func (p *RedisPool) HGet(key string, HKey string) (interface{}, error) { return Do("HGET", key, HKey) } func (p *RedisPool) HMGet(key string, hashKeys ...string) ([]interface{}, error) { ret, err := Do("HMGET", key, hashKeys) if err != nil { return nil, err } reta, ok := ret.([]interface{}) if !ok { return nil, errors.New("result not an array") } return reta, nil } func (p *RedisPool) HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { if len(hashKeys) == 0 || len(hashKeys) != len(vals) { var ret interface{} return ret, errors.New("bad length") } input := []interface{}{key} for i, v := range hashKeys { input = append(input, v, vals[i]) } return Do("HMSET", input...) } func (p *RedisPool) HGetString(key string, HKey string) (string, error) { return redigo.String(Do("HGET", key, HKey)) } func (p *RedisPool) HGetFloat(key string, HKey string) (float64, error) { f, err := redigo.Float64(Do("HGET", key, HKey)) return float64(f), err } func (p *RedisPool) HGetInt(key string, HKey string) (int, error) { return redigo.Int(Do("HGET", key, HKey)) } func (p *RedisPool) HGetInt64(key string, HKey string) (int64, error) { return redigo.Int64(Do("HGET", key, HKey)) } func (p *RedisPool) HGetBool(key string, HKey string) (bool, error) { return redigo.Bool(Do("HGET", key, HKey)) } func (p *RedisPool) HDel(key string, HKey string) (interface{}, error) { return Do("HDEL", key, HKey) } func (p *RedisPool) HGetAll(key string) (map[string]interface{}, error) { vals, err := redigo.Values(Do("HGETALL", key)) if err != nil { return nil, err } num := len(vals) / 2 result := make(map[string]interface{}, num) for i := 0; i < num; i++ { key, _ := redigo.String(vals[2*i], nil) result[key] = vals[2*i+1] } return result, nil } // NOTE: Use this in production environment with extreme care. // Read more here:https://redigo.io/commands/keys func (p *RedisPool) Keys(pattern string) ([]string, error) { return redigo.Strings(Do("KEYS", pattern)) } func (p *RedisPool) HKeys(key string) ([]string, error) { return redigo.Strings(Do("HKEYS", key)) } func (p *RedisPool) Exists(key string) (bool, error) { count, err := redigo.Int(Do("EXISTS", key)) if count == 0 { return false, err } else { return true, err } } func (p *RedisPool) Incr(key string) (int64, error) { return redigo.Int64(Do("INCR", key)) } func (p *RedisPool) Decr(key string) (int64, error) { return redigo.Int64(Do("DECR", key)) } func (p *RedisPool) IncrBy(key string, incBy int64) (int64, error) { return redigo.Int64(Do("INCRBY", key, incBy)) } func (p *RedisPool) DecrBy(key string, decrBy int64) (int64, error) { return redigo.Int64(Do("DECRBY", key)) } func (p *RedisPool) IncrByFloat(key string, incBy float64) (float64, error) { return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) } func (p *RedisPool) DecrByFloat(key string, decrBy float64) (float64, error) { return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) } // use for message queue func (p *RedisPool) LPush(key string, data interface{}) (interface{}, error) { // set return Do("LPUSH", key, data) } func (p *RedisPool) LPop(key string) (interface{}, error) { return Do("LPOP", key) } func (p *RedisPool) LPopString(key string) (string, error) { return redigo.String(Do("LPOP", key)) } func (p *RedisPool) LPopFloat(key string) (float64, error) { f, err := redigo.Float64(Do("LPOP", key)) return float64(f), err } func (p *RedisPool) LPopInt(key string) (int, error) { return redigo.Int(Do("LPOP", key)) } func (p *RedisPool) LPopInt64(key string) (int64, error) { return redigo.Int64(Do("LPOP", key)) } func (p *RedisPool) RPush(key string, data interface{}) (interface{}, error) { // set return Do("RPUSH", key, data) } func (p *RedisPool) RPop(key string) (interface{}, error) { return Do("RPOP", key) } func (p *RedisPool) RPopString(key string) (string, error) { return redigo.String(Do("RPOP", key)) } func (p *RedisPool) RPopFloat(key string) (float64, error) { f, err := redigo.Float64(Do("RPOP", key)) return float64(f), err } func (p *RedisPool) RPopInt(key string) (int, error) { return redigo.Int(Do("RPOP", key)) } func (p *RedisPool) RPopInt64(key string) (int64, error) { return redigo.Int64(Do("RPOP", key)) } func (p *RedisPool) Scan(cursor int64, pattern string, count int64) (int64, []string, error) { var items []string var newCursor int64 values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) if err != nil { return 0, nil, err } values, err = redigo.Scan(values, &newCursor, &items) if err != nil { return 0, nil, err } return newCursor, items, nil }