github.com/binbinly/pkg@v0.0.11-0.20240321014439-f4fbf666eb0f/cache/redis.go (about) 1 package cache 2 3 import ( 4 "context" 5 "reflect" 6 "time" 7 8 "github.com/binbinly/pkg/logger" 9 "github.com/pkg/errors" 10 "github.com/redis/go-redis/v9" 11 ) 12 13 type redisCache struct { 14 client *redis.Client 15 opts Options 16 } 17 18 // NewRedisCache new redis cache 19 func NewRedisCache(client *redis.Client, opts ...Option) Cache { 20 o := NewOptions(opts...) 21 return &redisCache{ 22 client: client, 23 opts: o, 24 } 25 } 26 27 // Set cache 28 func (c *redisCache) Set(ctx context.Context, key string, val any, expiration time.Duration) error { 29 buf, err := c.opts.codec.Marshal(val) 30 if err != nil { 31 return errors.Wrapf(err, "[cache] marshal data err, value is %+v", val) 32 } 33 34 if expiration == 0 { 35 expiration = c.opts.expire 36 } 37 38 if err = c.client.Set(ctx, c.buildKey(key), buf, expiration).Err(); err != nil { 39 return errors.Wrapf(err, "[cache] redis set error") 40 } 41 return nil 42 } 43 44 // Get cache 45 func (c *redisCache) Get(ctx context.Context, key string, val any) error { 46 cacheKey := c.buildKey(key) 47 data, err := c.client.Get(ctx, cacheKey).Bytes() 48 if err != nil && err != redis.Nil { 49 return errors.Wrapf(err, "[cache] get data error from redis, key is %+v", cacheKey) 50 } 51 52 // 防止data为空时,Unmarshal报错 53 if string(data) == "" { 54 return nil 55 } 56 if string(data) == NotFoundPlaceholder { 57 return ErrPlaceholder 58 } 59 60 if err = c.opts.codec.Unmarshal(data, val); err != nil { 61 return errors.Wrapf(err, "[cache] unmarshal data error, key=%s, cacheKey=%s type=%v, data=%+v ", 62 key, cacheKey, reflect.TypeOf(val), string(data)) 63 } 64 return nil 65 } 66 67 // MultiSet 批量设置缓存 68 func (c *redisCache) MultiSet(ctx context.Context, m map[string]any, expiration time.Duration) error { 69 if len(m) == 0 { 70 return nil 71 } 72 if expiration == 0 { 73 expiration = c.opts.expire 74 } 75 // key-value是成对的,所以这里的容量是map的2倍 76 paris := make([]any, 0, 2*len(m)) 77 for key, value := range m { 78 buf, err := c.opts.codec.Marshal(value) 79 if err != nil { 80 continue 81 } 82 paris = append(paris, []byte(c.buildKey(key))) 83 paris = append(paris, buf) 84 } 85 86 if err := c.client.MSet(ctx, paris...).Err(); err != nil { 87 return errors.Wrapf(err, "[cache] redis multi set error") 88 } 89 // 设置过期时间 90 pipe := c.client.Pipeline() 91 for i := 0; i < len(paris); i = i + 2 { 92 switch paris[i].(type) { 93 case []byte: 94 pipe.Expire(ctx, string(paris[i].([]byte)), expiration) 95 } 96 } 97 if _, err := pipe.Exec(ctx); err != nil { 98 return errors.Wrapf(err, "[cache] redis multi set expire error") 99 } 100 101 return nil 102 } 103 104 // MultiGet 批量获取缓存 105 func (c *redisCache) MultiGet(ctx context.Context, keys []string, value any, obj func() any) error { 106 if len(keys) == 0 { 107 return nil 108 } 109 cacheKeys := make([]string, len(keys)) 110 for index, key := range keys { 111 cacheKeys[index] = c.buildKey(key) 112 } 113 values, err := c.client.MGet(ctx, cacheKeys...).Result() 114 if err != nil { 115 return errors.Wrapf(err, "[cache] redis MGet error, keys is %+v", keys) 116 } 117 118 // 通过反射注入到map 119 valueMap := reflect.ValueOf(value) 120 for i, val := range values { 121 if val == nil { 122 continue 123 } 124 object := obj() 125 if val.(string) == NotFoundPlaceholder { 126 valueMap.SetMapIndex(reflect.ValueOf(keys[i]), reflect.ValueOf(object)) 127 continue 128 } 129 130 if err = c.opts.codec.Unmarshal([]byte(val.(string)), &object); err != nil { 131 logger.Warnf("[cache] unmarshal data error: %+v, key=%s, type=%v val=%v", err, 132 keys[i], reflect.TypeOf(val), val) 133 continue 134 } 135 valueMap.SetMapIndex(reflect.ValueOf(keys[i]), reflect.ValueOf(object)) 136 } 137 return nil 138 } 139 140 // Del cache 141 func (c *redisCache) Del(ctx context.Context, keys ...string) error { 142 if len(keys) == 0 { 143 return nil 144 } 145 146 // 批量构建cacheKey 147 cacheKeys := make([]string, len(keys)) 148 for index, key := range keys { 149 cacheKeys[index] = c.buildKey(key) 150 } 151 152 if err := c.client.Del(ctx, cacheKeys...).Err(); err != nil { 153 return errors.Wrapf(err, "[cache] redis delete error, keys is %+v", keys) 154 } 155 return nil 156 } 157 158 // SetCacheWithNotFound 设置空值 159 func (c *redisCache) SetCacheWithNotFound(ctx context.Context, key string) error { 160 return c.client.Set(ctx, c.buildKey(key), NotFoundPlaceholder, DefaultNotFoundExpireTime).Err() 161 } 162 163 func (c *redisCache) buildKey(key string) string { 164 cacheKey, _ := BuildCacheKey(c.opts.prefix, key) 165 return cacheKey 166 }