fad81008 by astaxie

add cache module

1 parent 9a3b27f2
1 ## cache
2 cache is a golang cache manager. It can use cache for many adapters. The repo is inspired by `database/sql` .
3
4 ##How to install
5
6 go get github.com/astaxie/beego/cache
7
8
9 ##how many adapter support
10
11 Now this cache support memory/redis/memcache
12
13 ## how to use it
14 first you must import it
15
16
17 import (
18 "github.com/astaxie/beego/cache"
19 )
20
21 then init an Cache(memory adapter)
22
23 bm, err := NewCache("memory", `{"interval":60}`)
24
25 use it like this:
26
27 bm.Put("astaxie", 1, 10)
28 bm.Get("astaxie")
29 bm.IsExist("astaxie")
30 bm.Delete("astaxie")
31
32 ## memory adapter
33 memory adapter config like this:
34
35 {"interval":60}
36
37 interval means the gc time. The cache will every interval time to check wheather have item expired.
38
39 ## memcache adapter
40 memory adapter use the vitess's [memcache](code.google.com/p/vitess/go/memcache) client.
41
42 the config like this:
43
44 {"conn":"127.0.0.1:11211"}
45
46
47 ## redis adapter
48 redis adapter use the [redigo](github.com/garyburd/redigo/redis) client.
49
50 the config like this:
51
52 {"conn":":6039"}
...\ No newline at end of file ...\ No newline at end of file
1 package cache
2
3 import (
4 "fmt"
5 )
6
7 type Cache interface {
8 Get(key string) interface{}
9 Put(key string, val interface{}, timeout int) error
10 Delete(key string) error
11 IsExist(key string) bool
12 ClearAll() error
13 StartAndGC(config string) error
14 }
15
16 var adapters = make(map[string]Cache)
17
18 // Register makes a cache adapter available by the adapter name.
19 // If Register is called twice with the same name or if driver is nil,
20 // it panics.
21 func Register(name string, adapter Cache) {
22 if adapter == nil {
23 panic("cache: Register adapter is nil")
24 }
25 if _, dup := adapters[name]; dup {
26 panic("cache: Register called twice for adapter " + name)
27 }
28 adapters[name] = adapter
29 }
30
31 //config is json {"interval":360}
32 func NewCache(adapterName, config string) (Cache, error) {
33 adapter, ok := adapters[adapterName]
34 if !ok {
35 return nil, fmt.Errorf("cache: unknown adaptername %q (forgotten import?)", adapterName)
36 }
37 adapter.StartAndGC(config)
38 return adapter, nil
39 }
1 package cache
2
3 import (
4 "testing"
5 "time"
6 )
7
8 func Test_cache(t *testing.T) {
9 bm, err := NewCache("memory", `{"interval":60}`)
10 if err != nil {
11 t.Error("init err")
12 }
13 if err = bm.Put("astaxie", 1, 10); err != nil {
14 t.Error("set Error", err)
15 }
16 if !bm.IsExist("astaxie") {
17 t.Error("check err")
18 }
19
20 if v := bm.Get("astaxie"); v.(int) != 1 {
21 t.Error("get err")
22 }
23
24 time.Sleep(70 * time.Second)
25
26 if bm.IsExist("astaxie") {
27 t.Error("check err")
28 }
29
30 if err = bm.Put("astaxie", 1, 10); err != nil {
31 t.Error("set Error", err)
32 }
33
34 bm.Delete("astaxie")
35 if bm.IsExist("astaxie") {
36 t.Error("delete err")
37 }
38 }
1 package cache
2
3 import (
4 "code.google.com/p/vitess/go/memcache"
5 "encoding/json"
6 "errors"
7 )
8
9 type MemcacheCache struct {
10 c *memcache.Connection
11 conninfo string
12 }
13
14 func NewMemCache() *MemcacheCache {
15 return &MemcacheCache{}
16 }
17
18 func (rc *MemcacheCache) Get(key string) interface{} {
19 if rc.c == nil {
20 rc.c = rc.connectInit()
21 }
22 v, _, err := rc.c.Get(key)
23 if err != nil {
24 return nil
25 }
26 var contain interface{}
27 contain = v
28 return contain
29 }
30
31 func (rc *MemcacheCache) Put(key string, val interface{}, timeout int) error {
32 if rc.c == nil {
33 rc.c = rc.connectInit()
34 }
35 v, ok := val.(string)
36 if !ok {
37 return errors.New("val must string")
38 }
39 stored, err := rc.c.Set(key, 0, uint64(timeout), []byte(v))
40 if err == nil && stored == false {
41 return errors.New("stored fail")
42 }
43 return err
44 }
45
46 func (rc *MemcacheCache) Delete(key string) error {
47 if rc.c == nil {
48 rc.c = rc.connectInit()
49 }
50 _, err := rc.c.Delete(key)
51 return err
52 }
53
54 func (rc *MemcacheCache) IsExist(key string) bool {
55 if rc.c == nil {
56 rc.c = rc.connectInit()
57 }
58 v, _, err := rc.c.Get(key)
59 if err != nil {
60 return false
61 }
62 if len(v) == 0 {
63 return false
64 } else {
65 return true
66 }
67 return true
68 }
69
70 func (rc *MemcacheCache) ClearAll() error {
71 if rc.c == nil {
72 rc.c = rc.connectInit()
73 }
74 err := rc.c.FlushAll()
75 return err
76 }
77
78 func (rc *MemcacheCache) StartAndGC(config string) error {
79 var cf map[string]string
80 json.Unmarshal([]byte(config), &cf)
81 if _, ok := cf["conn"]; !ok {
82 return errors.New("config has no conn key")
83 }
84 rc.conninfo = cf["conn"]
85 rc.c = rc.connectInit()
86 if rc.c == nil {
87 return errors.New("dial tcp conn error")
88 }
89 return nil
90 }
91
92 func (rc *MemcacheCache) connectInit() *memcache.Connection {
93 c, err := memcache.Connect(rc.conninfo)
94 if err != nil {
95 return nil
96 }
97 return c
98 }
99
100 func init() {
101 Register("memcache", NewMemCache())
102 }
1 package cache
2
3 import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "strconv"
8 "sync"
9 "time"
10 )
11
12 var (
13 DefaultEvery int = 60 // 1 minute
14 )
15
16 type MemoryItem struct {
17 val interface{}
18 Lastaccess time.Time
19 expired int
20 }
21
22 func (itm *MemoryItem) Access() interface{} {
23 itm.Lastaccess = time.Now()
24 return itm.val
25 }
26
27 type MemoryCache struct {
28 lock sync.RWMutex
29 dur time.Duration
30 items map[string]*MemoryItem
31 Every int // Run an expiration check Every seconds
32 }
33
34 // NewDefaultCache returns a new FileCache with sane defaults.
35 func NewMemoryCache() *MemoryCache {
36 cache := MemoryCache{items: make(map[string]*MemoryItem)}
37 return &cache
38 }
39
40 func (bc *MemoryCache) Get(name string) interface{} {
41 bc.lock.RLock()
42 defer bc.lock.RUnlock()
43 itm, ok := bc.items[name]
44 if !ok {
45 return nil
46 }
47 return itm.Access()
48 }
49
50 func (bc *MemoryCache) Put(name string, value interface{}, expired int) error {
51 bc.lock.Lock()
52 defer bc.lock.Unlock()
53 t := MemoryItem{val: value, Lastaccess: time.Now(), expired: expired}
54 if _, ok := bc.items[name]; ok {
55 return errors.New("the key is exist")
56 } else {
57 bc.items[name] = &t
58 }
59 return nil
60 }
61
62 func (bc *MemoryCache) Delete(name string) error {
63 bc.lock.Lock()
64 defer bc.lock.Unlock()
65 if _, ok := bc.items[name]; !ok {
66 return errors.New("key not exist")
67 }
68 delete(bc.items, name)
69 _, valid := bc.items[name]
70 if valid {
71 return errors.New("delete key error")
72 }
73 return nil
74 }
75
76 func (bc *MemoryCache) IsExist(name string) bool {
77 bc.lock.RLock()
78 defer bc.lock.RUnlock()
79 _, ok := bc.items[name]
80 return ok
81 }
82
83 func (bc *MemoryCache) ClearAll() error {
84 bc.lock.Lock()
85 defer bc.lock.Unlock()
86 bc.items = make(map[string]*MemoryItem)
87 return nil
88 }
89
90 // Start activates the file cache; it will
91 func (bc *MemoryCache) StartAndGC(config string) error {
92 var cf map[string]int
93 json.Unmarshal([]byte(config), &cf)
94 if _, ok := cf["every"]; !ok {
95 cf["interval"] = DefaultEvery
96 }
97 dur, err := time.ParseDuration(fmt.Sprintf("%ds", cf["interval"]))
98 if err != nil {
99 return err
100 }
101 bc.Every = cf["interval"]
102 bc.dur = dur
103 go bc.vaccuum()
104 return nil
105 }
106
107 func (bc *MemoryCache) vaccuum() {
108 if bc.Every < 1 {
109 return
110 }
111 for {
112 <-time.After(time.Duration(bc.dur))
113 if bc.items == nil {
114 return
115 }
116 for name, _ := range bc.items {
117 bc.item_expired(name)
118 }
119 }
120 }
121
122 // item_expired returns true if an item is expired.
123 func (bc *MemoryCache) item_expired(name string) bool {
124 bc.lock.Lock()
125 defer bc.lock.Unlock()
126 itm, ok := bc.items[name]
127 if !ok {
128 return true
129 }
130 dur := time.Now().Sub(itm.Lastaccess)
131 sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds()))
132 if err != nil {
133 delete(bc.items, name)
134 return true
135 } else if sec >= itm.expired {
136 delete(bc.items, name)
137 return true
138 }
139 return false
140 }
141
142 func init() {
143 Register("memory", NewMemoryCache())
144 }
1 package cache
2
3 import (
4 "encoding/json"
5 "errors"
6 "github.com/garyburd/redigo/redis"
7 )
8
9 var (
10 DefaultKey string = "beecacheRedis"
11 )
12
13 type RedisCache struct {
14 c redis.Conn
15 conninfo string
16 key string
17 }
18
19 func NewRedisCache() *RedisCache {
20 return &RedisCache{key: DefaultKey}
21 }
22
23 func (rc *RedisCache) Get(key string) interface{} {
24 if rc.c == nil {
25 rc.c = rc.connectInit()
26 }
27 v, err := rc.c.Do("HGET", rc.key, key)
28 if err != nil {
29 return nil
30 }
31 return v
32 }
33
34 func (rc *RedisCache) Put(key string, val interface{}, timeout int) error {
35 if rc.c == nil {
36 rc.c = rc.connectInit()
37 }
38 _, err := rc.c.Do("HSET", rc.key, key, val)
39 return err
40 }
41
42 func (rc *RedisCache) Delete(key string) error {
43 if rc.c == nil {
44 rc.c = rc.connectInit()
45 }
46 _, err := rc.c.Do("HDEL", rc.key, key)
47 return err
48 }
49
50 func (rc *RedisCache) IsExist(key string) bool {
51 if rc.c == nil {
52 rc.c = rc.connectInit()
53 }
54 v, err := redis.Bool(rc.c.Do("HEXISTS", rc.key, key))
55 if err != nil {
56 return false
57 }
58 return v
59 }
60
61 func (rc *RedisCache) ClearAll() error {
62 if rc.c == nil {
63 rc.c = rc.connectInit()
64 }
65 _, err := rc.c.Do("DEL", rc.key)
66 return err
67 }
68
69 func (rc *RedisCache) StartAndGC(config string) error {
70 var cf map[string]string
71 json.Unmarshal([]byte(config), &cf)
72 if _, ok := cf["key"]; !ok {
73 cf["key"] = DefaultKey
74 }
75 if _, ok := cf["conn"]; !ok {
76 return errors.New("config has no conn key")
77 }
78 rc.key = cf["key"]
79 rc.conninfo = cf["conn"]
80 rc.c = rc.connectInit()
81 if rc.c == nil {
82 return errors.New("dial tcp conn error")
83 }
84 return nil
85 }
86
87 func (rc *RedisCache) connectInit() redis.Conn {
88 c, err := redis.Dial("tcp", rc.conninfo)
89 if err != nil {
90 return nil
91 }
92 return c
93 }
94
95 func init() {
96 Register("redis", NewRedisCache())
97 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!