mirror of
https://github.com/astaxie/beego.git
synced 2024-11-25 20:00:55 +00:00
add cache module
This commit is contained in:
parent
9a3b27f29a
commit
fad8100804
52
cache/README.md
vendored
Normal file
52
cache/README.md
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
## cache
|
||||||
|
cache is a golang cache manager. It can use cache for many adapters. The repo is inspired by `database/sql` .
|
||||||
|
|
||||||
|
##How to install
|
||||||
|
|
||||||
|
go get github.com/astaxie/beego/cache
|
||||||
|
|
||||||
|
|
||||||
|
##how many adapter support
|
||||||
|
|
||||||
|
Now this cache support memory/redis/memcache
|
||||||
|
|
||||||
|
## how to use it
|
||||||
|
first you must import it
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
then init an Cache(memory adapter)
|
||||||
|
|
||||||
|
bm, err := NewCache("memory", `{"interval":60}`)
|
||||||
|
|
||||||
|
use it like this:
|
||||||
|
|
||||||
|
bm.Put("astaxie", 1, 10)
|
||||||
|
bm.Get("astaxie")
|
||||||
|
bm.IsExist("astaxie")
|
||||||
|
bm.Delete("astaxie")
|
||||||
|
|
||||||
|
## memory adapter
|
||||||
|
memory adapter config like this:
|
||||||
|
|
||||||
|
{"interval":60}
|
||||||
|
|
||||||
|
interval means the gc time. The cache will every interval time to check wheather have item expired.
|
||||||
|
|
||||||
|
## memcache adapter
|
||||||
|
memory adapter use the vitess's [memcache](code.google.com/p/vitess/go/memcache) client.
|
||||||
|
|
||||||
|
the config like this:
|
||||||
|
|
||||||
|
{"conn":"127.0.0.1:11211"}
|
||||||
|
|
||||||
|
|
||||||
|
## redis adapter
|
||||||
|
redis adapter use the [redigo](github.com/garyburd/redigo/redis) client.
|
||||||
|
|
||||||
|
the config like this:
|
||||||
|
|
||||||
|
{"conn":":6039"}
|
39
cache/cache.go
vendored
Normal file
39
cache/cache.go
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache interface {
|
||||||
|
Get(key string) interface{}
|
||||||
|
Put(key string, val interface{}, timeout int) error
|
||||||
|
Delete(key string) error
|
||||||
|
IsExist(key string) bool
|
||||||
|
ClearAll() error
|
||||||
|
StartAndGC(config string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var adapters = make(map[string]Cache)
|
||||||
|
|
||||||
|
// Register makes a cache adapter available by the adapter name.
|
||||||
|
// If Register is called twice with the same name or if driver is nil,
|
||||||
|
// it panics.
|
||||||
|
func Register(name string, adapter Cache) {
|
||||||
|
if adapter == nil {
|
||||||
|
panic("cache: Register adapter is nil")
|
||||||
|
}
|
||||||
|
if _, dup := adapters[name]; dup {
|
||||||
|
panic("cache: Register called twice for adapter " + name)
|
||||||
|
}
|
||||||
|
adapters[name] = adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
//config is json {"interval":360}
|
||||||
|
func NewCache(adapterName, config string) (Cache, error) {
|
||||||
|
adapter, ok := adapters[adapterName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cache: unknown adaptername %q (forgotten import?)", adapterName)
|
||||||
|
}
|
||||||
|
adapter.StartAndGC(config)
|
||||||
|
return adapter, nil
|
||||||
|
}
|
38
cache/cache_test.go
vendored
Normal file
38
cache/cache_test.go
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_cache(t *testing.T) {
|
||||||
|
bm, err := NewCache("memory", `{"interval":60}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("init err")
|
||||||
|
}
|
||||||
|
if err = bm.Put("astaxie", 1, 10); err != nil {
|
||||||
|
t.Error("set Error", err)
|
||||||
|
}
|
||||||
|
if !bm.IsExist("astaxie") {
|
||||||
|
t.Error("check err")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := bm.Get("astaxie"); v.(int) != 1 {
|
||||||
|
t.Error("get err")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(70 * time.Second)
|
||||||
|
|
||||||
|
if bm.IsExist("astaxie") {
|
||||||
|
t.Error("check err")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = bm.Put("astaxie", 1, 10); err != nil {
|
||||||
|
t.Error("set Error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bm.Delete("astaxie")
|
||||||
|
if bm.IsExist("astaxie") {
|
||||||
|
t.Error("delete err")
|
||||||
|
}
|
||||||
|
}
|
102
cache/memcache.go
vendored
Normal file
102
cache/memcache.go
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/vitess/go/memcache"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemcacheCache struct {
|
||||||
|
c *memcache.Connection
|
||||||
|
conninfo string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemCache() *MemcacheCache {
|
||||||
|
return &MemcacheCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) Get(key string) interface{} {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
v, _, err := rc.c.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var contain interface{}
|
||||||
|
contain = v
|
||||||
|
return contain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int) error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
v, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("val must string")
|
||||||
|
}
|
||||||
|
stored, err := rc.c.Set(key, 0, uint64(timeout), []byte(v))
|
||||||
|
if err == nil && stored == false {
|
||||||
|
return errors.New("stored fail")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) Delete(key string) error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
_, err := rc.c.Delete(key)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) IsExist(key string) bool {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
v, _, err := rc.c.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) ClearAll() error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
err := rc.c.FlushAll()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) StartAndGC(config string) error {
|
||||||
|
var cf map[string]string
|
||||||
|
json.Unmarshal([]byte(config), &cf)
|
||||||
|
if _, ok := cf["conn"]; !ok {
|
||||||
|
return errors.New("config has no conn key")
|
||||||
|
}
|
||||||
|
rc.conninfo = cf["conn"]
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
if rc.c == nil {
|
||||||
|
return errors.New("dial tcp conn error")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *MemcacheCache) connectInit() *memcache.Connection {
|
||||||
|
c, err := memcache.Connect(rc.conninfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("memcache", NewMemCache())
|
||||||
|
}
|
144
cache/memory.go
vendored
Normal file
144
cache/memory.go
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultEvery int = 60 // 1 minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemoryItem struct {
|
||||||
|
val interface{}
|
||||||
|
Lastaccess time.Time
|
||||||
|
expired int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itm *MemoryItem) Access() interface{} {
|
||||||
|
itm.Lastaccess = time.Now()
|
||||||
|
return itm.val
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryCache struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
dur time.Duration
|
||||||
|
items map[string]*MemoryItem
|
||||||
|
Every int // Run an expiration check Every seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultCache returns a new FileCache with sane defaults.
|
||||||
|
func NewMemoryCache() *MemoryCache {
|
||||||
|
cache := MemoryCache{items: make(map[string]*MemoryItem)}
|
||||||
|
return &cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) Get(name string) interface{} {
|
||||||
|
bc.lock.RLock()
|
||||||
|
defer bc.lock.RUnlock()
|
||||||
|
itm, ok := bc.items[name]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return itm.Access()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) Put(name string, value interface{}, expired int) error {
|
||||||
|
bc.lock.Lock()
|
||||||
|
defer bc.lock.Unlock()
|
||||||
|
t := MemoryItem{val: value, Lastaccess: time.Now(), expired: expired}
|
||||||
|
if _, ok := bc.items[name]; ok {
|
||||||
|
return errors.New("the key is exist")
|
||||||
|
} else {
|
||||||
|
bc.items[name] = &t
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) Delete(name string) error {
|
||||||
|
bc.lock.Lock()
|
||||||
|
defer bc.lock.Unlock()
|
||||||
|
if _, ok := bc.items[name]; !ok {
|
||||||
|
return errors.New("key not exist")
|
||||||
|
}
|
||||||
|
delete(bc.items, name)
|
||||||
|
_, valid := bc.items[name]
|
||||||
|
if valid {
|
||||||
|
return errors.New("delete key error")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) IsExist(name string) bool {
|
||||||
|
bc.lock.RLock()
|
||||||
|
defer bc.lock.RUnlock()
|
||||||
|
_, ok := bc.items[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) ClearAll() error {
|
||||||
|
bc.lock.Lock()
|
||||||
|
defer bc.lock.Unlock()
|
||||||
|
bc.items = make(map[string]*MemoryItem)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start activates the file cache; it will
|
||||||
|
func (bc *MemoryCache) StartAndGC(config string) error {
|
||||||
|
var cf map[string]int
|
||||||
|
json.Unmarshal([]byte(config), &cf)
|
||||||
|
if _, ok := cf["every"]; !ok {
|
||||||
|
cf["interval"] = DefaultEvery
|
||||||
|
}
|
||||||
|
dur, err := time.ParseDuration(fmt.Sprintf("%ds", cf["interval"]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bc.Every = cf["interval"]
|
||||||
|
bc.dur = dur
|
||||||
|
go bc.vaccuum()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *MemoryCache) vaccuum() {
|
||||||
|
if bc.Every < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
<-time.After(time.Duration(bc.dur))
|
||||||
|
if bc.items == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for name, _ := range bc.items {
|
||||||
|
bc.item_expired(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// item_expired returns true if an item is expired.
|
||||||
|
func (bc *MemoryCache) item_expired(name string) bool {
|
||||||
|
bc.lock.Lock()
|
||||||
|
defer bc.lock.Unlock()
|
||||||
|
itm, ok := bc.items[name]
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
dur := time.Now().Sub(itm.Lastaccess)
|
||||||
|
sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds()))
|
||||||
|
if err != nil {
|
||||||
|
delete(bc.items, name)
|
||||||
|
return true
|
||||||
|
} else if sec >= itm.expired {
|
||||||
|
delete(bc.items, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("memory", NewMemoryCache())
|
||||||
|
}
|
97
cache/redis.go
vendored
Normal file
97
cache/redis.go
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultKey string = "beecacheRedis"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RedisCache struct {
|
||||||
|
c redis.Conn
|
||||||
|
conninfo string
|
||||||
|
key string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedisCache() *RedisCache {
|
||||||
|
return &RedisCache{key: DefaultKey}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) Get(key string) interface{} {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
v, err := rc.c.Do("HGET", rc.key, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) Put(key string, val interface{}, timeout int) error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
_, err := rc.c.Do("HSET", rc.key, key, val)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) Delete(key string) error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
_, err := rc.c.Do("HDEL", rc.key, key)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) IsExist(key string) bool {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
v, err := redis.Bool(rc.c.Do("HEXISTS", rc.key, key))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) ClearAll() error {
|
||||||
|
if rc.c == nil {
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
}
|
||||||
|
_, err := rc.c.Do("DEL", rc.key)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) StartAndGC(config string) error {
|
||||||
|
var cf map[string]string
|
||||||
|
json.Unmarshal([]byte(config), &cf)
|
||||||
|
if _, ok := cf["key"]; !ok {
|
||||||
|
cf["key"] = DefaultKey
|
||||||
|
}
|
||||||
|
if _, ok := cf["conn"]; !ok {
|
||||||
|
return errors.New("config has no conn key")
|
||||||
|
}
|
||||||
|
rc.key = cf["key"]
|
||||||
|
rc.conninfo = cf["conn"]
|
||||||
|
rc.c = rc.connectInit()
|
||||||
|
if rc.c == nil {
|
||||||
|
return errors.New("dial tcp conn error")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RedisCache) connectInit() redis.Conn {
|
||||||
|
c, err := redis.Dial("tcp", rc.conninfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("redis", NewRedisCache())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user