mirror of
https://github.com/astaxie/beego.git
synced 2024-12-22 22:00:49 +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