beego解读Cache之MemoryCache

beego 框架 cache 目录是 Go 实现的一个缓存管理器,是beego框架自带工具之一,当然,如果你只想使用 cache 而不是整个 beego 框架,可以选择性安装:

go get github.com/astaxie/beego/cache

在我写这篇博文时, beego 版本是 v1.11.1 , cache 支持内存、文件、 memcache 、 redis、ssdb 。

使用例子:

package main

import (
    // 导入cache包
    "github.com/astaxie/beego/cache"
    "time"
)

func  main()  {
    /*
    bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)//memcache
    bm, err := cache.NewCache("redis", `{"conn":":6039"}`)//redis
    bm, err := cache.NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)

     */
    // 初始化一个内存的缓存管理器
    bm, err := cache.NewCache("memory", `{"interval":60}`)
    bm.Put("user", "张三", 10 * time.Second) //
    bm.Get("user")
    bm.IsExist("user")
    bm.Delete("user")
}

cache 包定义了Cache接口,如下:

// Cache interface contains all behaviors for cache adapter.
// usage:
//  cache.Register("file",cache.NewFileCache) // this operation is run in init method of file.go.
//  c,err := cache.NewCache("file","{....}")
//  c.Put("key",value, 3600 * time.Second)
//  v := c.Get("key")
//
//  c.Incr("counter")  // now is 1
//  c.Incr("counter")  // now is 2
//  count := c.Get("counter").(int)
type Cache interface {
    // Get 函数通过键获取值。
    Get(key string) interface{}
    // GetMulti 是 Get 的一个批处理版本。
    GetMulti(keys []string) []interface{}
    // Put 函数设置存入一对键值和到期时间。
    Put(key string, val interface{}, timeout time.Duration) error
    // 通过键删除值.
    Delete(key string) error
    // 让键对应的值加1.
    Incr(key string) error
    // 让键对应的值减1.
    Decr(key string) error
    // 检查一个键是否存在.
    IsExist(key string) bool
    // 清空所有缓存.
    ClearAll() error
    // 根据配置启动一个gc协程。
    StartAndGC(config string) error
}

cache.go文件定义了 Instance , Instance 是一个 function ,功能是创建一个Cache接口的实例。adapters是一个 map 用于注册实现了 Cache 的适配器。Register注册可用的适配器,如果被调用两次或者驱动为空,将 panic 。代码如下:

type Instance func() Cache
var adapters = make(map[string]Instance)
func Register(name string, adapter Instance) {
    if adapter == nil {
        panic("cache: Register adapter is nil")
    }
    if _, ok := adapters[name]; ok {
        panic("cache: Register called twice for adapter " + name)
    }
    adapters[name] = adapter
}

我们来先看内存缓存的实现方式:

1.MemoryCache memory,go

全局变量及结构

DefaultEvery 表示在内存中回收过期缓存项的时间,默认为 1 分钟。

var DefaultEvery = 60

MemoryItem 用于保存基本内存缓存元素.

type MemoryItem struct {
    val         interface{}
    createdTime time.Time
    lifespan    time.Duration
}

MemoryCache 是一个内存管理适配器,它包含了一个读写锁(sync.RWMutex),使得 MemoryCache 具备并发安全性。

type MemoryCache struct {
    sync.RWMutex
    dur   time.Duration
    items map[string]*MemoryItem
    Every int // run an expiration check Every clock time
}

方法及函数

init function 注册驱动名,其实就是一个map存取操作,见上面的 Register 函数。

func init() {
    Register("memory", NewMemoryCache)
}

NewMemoryCache 初始化一个内存缓存适配器,返回 Cache 接口,NewMemoryCache 实现了 Cache 接口。

func NewMemoryCache() Cache {
    cache := MemoryCache{items: make(map[string]*MemoryItem)}
    return &cache
}

Get 返回一个键在 MemoryCache 中的值, 如果该键不存在或到期,返回nil。

func (bc *MemoryCache) Get(name string) interface{} {
    bc.RLock()
    defer bc.RUnlock()
    
    if itm, ok := bc.items[name]; ok {
        if itm.isExpire() {
            return nil
        }
        return itm.val
    }
    return nil
}

GetMulti 传入一个键的 slice,返回slice中存在于缓存且没有过期的值.

func (bc *MemoryCache) GetMulti(names []string) []interface{} {
    var rc []interface{}
    for _, name := range names {
        rc = append(rc, bc.Get(name))
    }
    return rc
}

Put 向 MemoryCache 中存入一个键名位name ,值为 item 的缓存键值对并设置时间为 lifespan ,如果时间为 0 ,那么 item 将永远存在,除非用户主动删除或。

func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error {
    bc.Lock()
    defer bc.Unlock()
    bc.items[name] = &MemoryItem{
        val:         value,
        createdTime: time.Now(),
        lifespan:    lifespan,
    }
    return nil
}

Delete 删除 MemoryCache 中键名为 name 的缓存.

func (bc *MemoryCache) Delete(name string) error {
    bc.Lock()
    defer bc.Unlock()
    if _, ok := bc.items[name]; !ok {
        return errors.New("key not exist")
    }
    delete(bc.items, name)
    if _, ok := bc.items[name]; ok {
        return errors.New("delete key error")
    }
    return nil
}

Incr 增加键名为 name 缓存的值,支持类型:int,int32,int64,uint,uint32,uint64.

func (bc *MemoryCache) Incr(key string) error {
    bc.RLock()
    defer bc.RUnlock()
    itm, ok := bc.items[key]
    if !ok {
        return errors.New("key not exist")
    }
    switch itm.val.(type) {
    case int:
        itm.val = itm.val.(int) + 1
    case int32:
        itm.val = itm.val.(int32) + 1
    case int64:
        itm.val = itm.val.(int64) + 1
    case uint:
        itm.val = itm.val.(uint) + 1
    case uint32:
        itm.val = itm.val.(uint32) + 1
    case uint64:
        itm.val = itm.val.(uint64) + 1
    default:
        return errors.New("item val is not (u)int (u)int32 (u)int64")
    }
    return nil
}

Decr 减少键名为 name 缓存的值,支持类型:int,int32,int64,uint,uint32,uint64,如果类型为 uint,uint32,uint64 且值为 0 时,会返回值小于0错误。

func (bc *MemoryCache) Decr(key string) error {
    bc.RLock()
    defer bc.RUnlock()
    itm, ok := bc.items[key]
    if !ok {
        return errors.New("key not exist")
    }
    switch itm.val.(type) {
    case int:
        itm.val = itm.val.(int) - 1
    case int64:
        itm.val = itm.val.(int64) - 1
    case int32:
        itm.val = itm.val.(int32) - 1
    case uint:
        if itm.val.(uint) > 0 {
            itm.val = itm.val.(uint) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    case uint32:
        if itm.val.(uint32) > 0 {
            itm.val = itm.val.(uint32) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    case uint64:
        if itm.val.(uint64) > 0 {
            itm.val = itm.val.(uint64) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    default:
        return errors.New("item val is not int int64 int32")
    }
    return nil
}

IsExist 检查键为 name 的缓存是否存在.

func (bc *MemoryCache) IsExist(name string) bool {
    bc.RLock()
    defer bc.RUnlock()
    if v, ok := bc.items[name]; ok {
        return !v.isExpire()
    }
    return false
}

ClearAll 会清除所有缓存.


func (bc *MemoryCache) ClearAll() error {
    bc.Lock()
    defer bc.Unlock()
    bc.items = make(map[string]*MemoryItem)
    return nil
}

StartAndGC 开始周期性对缓存进行检查,如果缓存键值对过期,会被删除。

func (bc *MemoryCache) StartAndGC(config string) error {
    var cf map[string]int
    json.Unmarshal([]byte(config), &cf)
    if _, ok := cf["interval"]; !ok {
        cf = make(map[string]int)
        cf["interval"] = DefaultEvery
    }
    dur := time.Duration(cf["interval"]) * time.Second
    bc.Every = cf["interval"]
    bc.dur = dur
    go bc.vacuum()
    return nil
}

未导出函数及方法

// 检查是否超时.
func (bc *MemoryCache) vacuum() {
    bc.RLock()
    every := bc.Every
    bc.RUnlock()

    if every < 1 {
        return
    }
    for {
        <-time.After(bc.dur)
        if bc.items == nil {
            return
        }
        if keys := bc.expiredKeys(); len(keys) != 0 {
            bc.clearItems(keys)
        }
    }
}

// expiredKeys 返回到期的键名 slice.
func (bc *MemoryCache) expiredKeys() (keys []string) {
    bc.RLock()
    defer bc.RUnlock()
    for key, itm := range bc.items {
        if itm.isExpire() {
            keys = append(keys, key)
        }
    }
    return
}

// clearItems 清空键名在 keys 内的缓存.
func (bc *MemoryCache) clearItems(keys []string) {
    bc.Lock()
    defer bc.Unlock()
    for _, key := range keys {
        delete(bc.items, key)
    }
}

后记

file、ssdb、memcache和redis实现都相似,file以文件方式储存每个键对应一个文件,文件内容为值的数据,用gob编码持久化。

猜你喜欢

转载自www.cnblogs.com/asp-poor-as-john/p/go_beego_cache_memorycache.html