github.com/astaxie/beego@v1.12.3/cache/redis/redis.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package redis for cache provider 16 // 17 // depend on github.com/gomodule/redigo/redis 18 // 19 // go install github.com/gomodule/redigo/redis 20 // 21 // Usage: 22 // import( 23 // _ "github.com/astaxie/beego/cache/redis" 24 // "github.com/astaxie/beego/cache" 25 // ) 26 // 27 // bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`) 28 // 29 // more docs http://beego.me/docs/module/cache.md 30 package redis 31 32 import ( 33 "encoding/json" 34 "errors" 35 "fmt" 36 "strconv" 37 "time" 38 39 "github.com/gomodule/redigo/redis" 40 41 "strings" 42 43 "github.com/astaxie/beego/cache" 44 ) 45 46 var ( 47 // DefaultKey the collection name of redis for cache adapter. 48 DefaultKey = "beecacheRedis" 49 ) 50 51 // Cache is Redis cache adapter. 52 type Cache struct { 53 p *redis.Pool // redis connection pool 54 conninfo string 55 dbNum int 56 key string 57 password string 58 maxIdle int 59 60 //the timeout to a value less than the redis server's timeout. 61 timeout time.Duration 62 } 63 64 // NewRedisCache create new redis cache with default collection name. 65 func NewRedisCache() cache.Cache { 66 return &Cache{key: DefaultKey} 67 } 68 69 // actually do the redis cmds, args[0] must be the key name. 70 func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) { 71 if len(args) < 1 { 72 return nil, errors.New("missing required arguments") 73 } 74 args[0] = rc.associate(args[0]) 75 c := rc.p.Get() 76 defer c.Close() 77 78 return c.Do(commandName, args...) 79 } 80 81 // associate with config key. 82 func (rc *Cache) associate(originKey interface{}) string { 83 return fmt.Sprintf("%s:%s", rc.key, originKey) 84 } 85 86 // Get cache from redis. 87 func (rc *Cache) Get(key string) interface{} { 88 if v, err := rc.do("GET", key); err == nil { 89 return v 90 } 91 return nil 92 } 93 94 // GetMulti get cache from redis. 95 func (rc *Cache) GetMulti(keys []string) []interface{} { 96 c := rc.p.Get() 97 defer c.Close() 98 var args []interface{} 99 for _, key := range keys { 100 args = append(args, rc.associate(key)) 101 } 102 values, err := redis.Values(c.Do("MGET", args...)) 103 if err != nil { 104 return nil 105 } 106 return values 107 } 108 109 // Put put cache to redis. 110 func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { 111 _, err := rc.do("SETEX", key, int64(timeout/time.Second), val) 112 return err 113 } 114 115 // Delete delete cache in redis. 116 func (rc *Cache) Delete(key string) error { 117 _, err := rc.do("DEL", key) 118 return err 119 } 120 121 // IsExist check cache's existence in redis. 122 func (rc *Cache) IsExist(key string) bool { 123 v, err := redis.Bool(rc.do("EXISTS", key)) 124 if err != nil { 125 return false 126 } 127 return v 128 } 129 130 // Incr increase counter in redis. 131 func (rc *Cache) Incr(key string) error { 132 _, err := redis.Bool(rc.do("INCRBY", key, 1)) 133 return err 134 } 135 136 // Decr decrease counter in redis. 137 func (rc *Cache) Decr(key string) error { 138 _, err := redis.Bool(rc.do("INCRBY", key, -1)) 139 return err 140 } 141 142 // ClearAll clean all cache in redis. delete this redis collection. 143 func (rc *Cache) ClearAll() error { 144 cachedKeys, err := rc.Scan(rc.key + ":*") 145 if err != nil { 146 return err 147 } 148 c := rc.p.Get() 149 defer c.Close() 150 for _, str := range cachedKeys { 151 if _, err = c.Do("DEL", str); err != nil { 152 return err 153 } 154 } 155 return err 156 } 157 158 // Scan scan all keys matching the pattern. a better choice than `keys` 159 func (rc *Cache) Scan(pattern string) (keys []string, err error) { 160 c := rc.p.Get() 161 defer c.Close() 162 var ( 163 cursor uint64 = 0 // start 164 result []interface{} 165 list []string 166 ) 167 for { 168 result, err = redis.Values(c.Do("SCAN", cursor, "MATCH", pattern, "COUNT", 1024)) 169 if err != nil { 170 return 171 } 172 list, err = redis.Strings(result[1], nil) 173 if err != nil { 174 return 175 } 176 keys = append(keys, list...) 177 cursor, err = redis.Uint64(result[0], nil) 178 if err != nil { 179 return 180 } 181 if cursor == 0 { // over 182 return 183 } 184 } 185 } 186 187 // StartAndGC start redis cache adapter. 188 // config is like {"key":"collection key","conn":"connection info","dbNum":"0"} 189 // the cache item in redis are stored forever, 190 // so no gc operation. 191 func (rc *Cache) StartAndGC(config string) error { 192 var cf map[string]string 193 json.Unmarshal([]byte(config), &cf) 194 195 if _, ok := cf["key"]; !ok { 196 cf["key"] = DefaultKey 197 } 198 if _, ok := cf["conn"]; !ok { 199 return errors.New("config has no conn key") 200 } 201 202 // Format redis://<password>@<host>:<port> 203 cf["conn"] = strings.Replace(cf["conn"], "redis://", "", 1) 204 if i := strings.Index(cf["conn"], "@"); i > -1 { 205 cf["password"] = cf["conn"][0:i] 206 cf["conn"] = cf["conn"][i+1:] 207 } 208 209 if _, ok := cf["dbNum"]; !ok { 210 cf["dbNum"] = "0" 211 } 212 if _, ok := cf["password"]; !ok { 213 cf["password"] = "" 214 } 215 if _, ok := cf["maxIdle"]; !ok { 216 cf["maxIdle"] = "3" 217 } 218 if _, ok := cf["timeout"]; !ok { 219 cf["timeout"] = "180s" 220 } 221 rc.key = cf["key"] 222 rc.conninfo = cf["conn"] 223 rc.dbNum, _ = strconv.Atoi(cf["dbNum"]) 224 rc.password = cf["password"] 225 rc.maxIdle, _ = strconv.Atoi(cf["maxIdle"]) 226 227 if v, err := time.ParseDuration(cf["timeout"]); err == nil { 228 rc.timeout = v 229 } else { 230 rc.timeout = 180 * time.Second 231 } 232 233 rc.connectInit() 234 235 c := rc.p.Get() 236 defer c.Close() 237 238 return c.Err() 239 } 240 241 // connect to redis. 242 func (rc *Cache) connectInit() { 243 dialFunc := func() (c redis.Conn, err error) { 244 c, err = redis.Dial("tcp", rc.conninfo) 245 if err != nil { 246 return nil, err 247 } 248 249 if rc.password != "" { 250 if _, err := c.Do("AUTH", rc.password); err != nil { 251 c.Close() 252 return nil, err 253 } 254 } 255 256 _, selecterr := c.Do("SELECT", rc.dbNum) 257 if selecterr != nil { 258 c.Close() 259 return nil, selecterr 260 } 261 return 262 } 263 // initialize a new pool 264 rc.p = &redis.Pool{ 265 MaxIdle: rc.maxIdle, 266 IdleTimeout: rc.timeout, 267 Dial: dialFunc, 268 } 269 } 270 271 func init() { 272 cache.Register("redis", NewRedisCache) 273 }