You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.1 KiB

  1. // Copyright 2013 Beego Authors
  2. // Copyright 2014 The Macaron Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  5. // not use this file except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. package cache
  16. import (
  17. "fmt"
  18. "strings"
  19. "time"
  20. "github.com/Unknwon/com"
  21. "gopkg.in/ini.v1"
  22. "gopkg.in/redis.v2"
  23. "github.com/go-macaron/cache"
  24. )
  25. // RedisCacher represents a redis cache adapter implementation.
  26. type RedisCacher struct {
  27. c *redis.Client
  28. prefix string
  29. hsetName string
  30. occupyMode bool
  31. }
  32. // Put puts value into cache with key and expire time.
  33. // If expired is 0, it lives forever.
  34. func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
  35. key = c.prefix + key
  36. if expire == 0 {
  37. if err := c.c.Set(key, com.ToStr(val)).Err(); err != nil {
  38. return err
  39. }
  40. } else {
  41. dur, err := time.ParseDuration(com.ToStr(expire) + "s")
  42. if err != nil {
  43. return err
  44. }
  45. if err = c.c.SetEx(key, dur, com.ToStr(val)).Err(); err != nil {
  46. return err
  47. }
  48. }
  49. if c.occupyMode {
  50. return nil
  51. }
  52. return c.c.HSet(c.hsetName, key, "0").Err()
  53. }
  54. // Get gets cached value by given key.
  55. func (c *RedisCacher) Get(key string) interface{} {
  56. val, err := c.c.Get(c.prefix + key).Result()
  57. if err != nil {
  58. return nil
  59. }
  60. return val
  61. }
  62. // Delete deletes cached value by given key.
  63. func (c *RedisCacher) Delete(key string) error {
  64. key = c.prefix + key
  65. if err := c.c.Del(key).Err(); err != nil {
  66. return err
  67. }
  68. if c.occupyMode {
  69. return nil
  70. }
  71. return c.c.HDel(c.hsetName, key).Err()
  72. }
  73. // Incr increases cached int-type value by given key as a counter.
  74. func (c *RedisCacher) Incr(key string) error {
  75. if !c.IsExist(key) {
  76. return fmt.Errorf("key '%s' not exist", key)
  77. }
  78. return c.c.Incr(c.prefix + key).Err()
  79. }
  80. // Decr decreases cached int-type value by given key as a counter.
  81. func (c *RedisCacher) Decr(key string) error {
  82. if !c.IsExist(key) {
  83. return fmt.Errorf("key '%s' not exist", key)
  84. }
  85. return c.c.Decr(c.prefix + key).Err()
  86. }
  87. // IsExist returns true if cached value exists.
  88. func (c *RedisCacher) IsExist(key string) bool {
  89. if c.c.Exists(c.prefix + key).Val() {
  90. return true
  91. }
  92. if !c.occupyMode {
  93. c.c.HDel(c.hsetName, c.prefix+key)
  94. }
  95. return false
  96. }
  97. // Flush deletes all cached data.
  98. func (c *RedisCacher) Flush() error {
  99. if c.occupyMode {
  100. return c.c.FlushDb().Err()
  101. }
  102. keys, err := c.c.HKeys(c.hsetName).Result()
  103. if err != nil {
  104. return err
  105. }
  106. if err = c.c.Del(keys...).Err(); err != nil {
  107. return err
  108. }
  109. return c.c.Del(c.hsetName).Err()
  110. }
  111. // StartAndGC starts GC routine based on config string settings.
  112. // AdapterConfig: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
  113. func (c *RedisCacher) StartAndGC(opts cache.Options) error {
  114. c.hsetName = "MacaronCache"
  115. c.occupyMode = opts.OccupyMode
  116. cfg, err := ini.Load([]byte(strings.Replace(opts.AdapterConfig, ",", "\n", -1)))
  117. if err != nil {
  118. return err
  119. }
  120. opt := &redis.Options{
  121. Network: "tcp",
  122. }
  123. for k, v := range cfg.Section("").KeysHash() {
  124. switch k {
  125. case "network":
  126. opt.Network = v
  127. case "addr":
  128. opt.Addr = v
  129. case "password":
  130. opt.Password = v
  131. case "db":
  132. opt.DB = com.StrTo(v).MustInt64()
  133. case "pool_size":
  134. opt.PoolSize = com.StrTo(v).MustInt()
  135. case "idle_timeout":
  136. opt.IdleTimeout, err = time.ParseDuration(v + "s")
  137. if err != nil {
  138. return fmt.Errorf("error parsing idle timeout: %v", err)
  139. }
  140. case "hset_name":
  141. c.hsetName = v
  142. case "prefix":
  143. c.prefix = v
  144. default:
  145. return fmt.Errorf("session/redis: unsupported option '%s'", k)
  146. }
  147. }
  148. c.c = redis.NewClient(opt)
  149. if err = c.c.Ping().Err(); err != nil {
  150. return err
  151. }
  152. return nil
  153. }
  154. func init() {
  155. cache.Register("redis", &RedisCacher{})
  156. }