beego

beego cache模块源码分析笔记一

2018-12-14  本文已影响1人  ljh123

cache模块是beego中的一个独立模块,这个模块的设计方式是经典的工厂模式。

cache.go:抽象接口部分、注册部分、模块以外的初始化。
memory:memorycache
file:filecache
cache/memcache:封装的是memcache
cache/redis:封装的是redis
cache/ssdb:封装的是ssdb

image.png
分析下cache文件源码
这个文件里面定义了一个Cache接口,一个注册cache驱动、一个创建新的Cache、新定义了一个函数类型func() Cache、var adapters = make(map[string]Instance)变量

(1)Cache接口

type Cache interface {
    Get(key string) interface{} 
    GetMulti(keys []string) []interface{}
    Put(key string, val interface{}, timeout time.Duration) error  
    Delete(key string)
    Incr(key string) error
    Decr(key string) error
    IsExist(key string) bool
    ClearAll() error
    StartAndGC(config string) error
}
type Instance func() Cache

var adapters = make(map[string]Instance)

注册接口:Register

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
}

对该代码进行分析,将传入name和适配器(memory、file、redis等)之间建立映射关系(单例模式)。
三种情况:
1)适配器为空,panic异常
2)name已经注册了,panic异常
3)注册成功,建立名称与适配器间的关系

新建一个Cache:NewCache

func NewCache(adapterName, config string) (adapter Cache, err error) {
    instanceFunc, ok := adapters[adapterName]
    if !ok {
        err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
        return
    }
    adapter = instanceFunc()
    err = adapter.StartAndGC(config)
    if err != nil {
        adapter = nil
    }
    return
}

根据之前注册过的适配器,如果之前没有注册到适配器中,则会报错,很像单例模式的实现,创建一个Cache变量,并设置cahce gc调度器。

以上就是这个文件的全部内容
接着分析file.go文件
这个文件实现以文件存储的方式实现了Cache接口

func init() {
    Register("file", NewFileCache)
}

自动注册将fileCache到适配器中

type FileCacheItem struct {
    Data       interface{}  // 数据
    Lastaccess time.Time    // 最后一次访问时间
    Expired    time.Time    // 过期时间
}

FileCacheItem是文件缓存适配器的基本单元

type FileCache struct {
    CachePath      string
    FileSuffix     string
    DirectoryLevel int
    EmbedExpiry    int
}

FileCache是​​用于文件存储的缓存适配器

var (
    FileCachePath           = "cache"     // cache 目录
    FileCacheFileSuffix     = ".bin"       // cache 文件后缀
    FileCacheDirectoryLevel = 2         // file文件存储目录层级
    FileCacheEmbedExpiry    time.Duration // 过期时间,默认不过期
)
func NewFileCache() Cache {
    //    return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix}
    return &FileCache{}
}

创建一个文件Cache,可以根据需求,设置为文件配置,或者是后面再添加配置

func (fc *FileCache) StartAndGC(config string) error {
    var cfg map[string]string
        // 传入的config信息是一个json字符串,没有放入的内容会被选择成默认的配置信息
    json.Unmarshal([]byte(config), &cfg)
    if _, ok := cfg["CachePath"]; !ok {
        cfg["CachePath"] = FileCachePath
    }
    if _, ok := cfg["FileSuffix"]; !ok {
        cfg["FileSuffix"] = FileCacheFileSuffix
    }
    if _, ok := cfg["DirectoryLevel"]; !ok {
        cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel)
    }
    if _, ok := cfg["EmbedExpiry"]; !ok {
        cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
    }
    fc.CachePath = cfg["CachePath"]
    fc.FileSuffix = cfg["FileSuffix"]
    fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
    fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])

    fc.Init()  // 初始化,如果设置的file存储路径不存在,则创建这个路径
    return nil
}

func (fc *FileCache) Init() {
    if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
        _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
    }
}
func (fc *FileCache) getCacheFileName(key string) string {
    m := md5.New()
    io.WriteString(m, key)
    keyMd5 := hex.EncodeToString(m.Sum(nil))  // key的md5值
    cachePath := fc.CachePath
    switch fc.DirectoryLevel {
    case 2:
        cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4])
    case 1:
        cachePath = filepath.Join(cachePath, keyMd5[0:2])
    }

    if ok, _ := exists(cachePath); !ok { // todo : error handle
        _ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
    }
  
    return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
}

创建cache文件存储名称,先对key进行md5操作,然后根据DirectoryLevel的值构造目录,值为1则只创建一层,为2则再加上一层,之后将构造好的文件名返回。

func (fc *FileCache) Get(key string) interface{} {
    fileData, err := FileGetContents(fc.getCacheFileName(key))
    if err != nil {
        return ""
    }
    var to FileCacheItem
    GobDecode(fileData, &to)  // 从这里我们可以得出,beego中file存储是以Gob的方式进行存储的
    if to.Expired.Before(time.Now()) {  // 如果过期,则返回空
        return ""
    }
    return to.Data  // 返回gob解码的数据
}
// FileGetContents Get bytes to file.从这个文件中获取内容,以[]byte形式返回
// if non-exist, create this file.如果文件不存在,则创建这个文件
func FileGetContents(filename string) (data []byte, e error) {
    f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
    if e != nil {
        return
    }
    defer f.Close()
    stat, e := f.Stat()
    if e != nil {
        return
    }
    data = make([]byte, stat.Size())
    result, e := f.Read(data)
    if e != nil || int64(result) != stat.Size() {
        return nil, e
    }
    return
}

通过key,获取数据

// GetMulti gets values from file cache.
// if non-exist or expired, return empty string.
func (fc *FileCache) GetMulti(keys []string) []interface{} {
    var rc []interface{}
    for _, key := range keys {
        rc = append(rc, fc.Get(key))
    }
    return rc
}

通过key,获取一组数据

// Put value into file cache.
// timeout means how long to keep this file, unit of ms.
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever.
func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error {
    gob.Register(val)  // 编码前注册

    item := FileCacheItem{Data: val}
    if timeout == FileCacheEmbedExpiry {
        item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years,如果过期时间设置成与默认配置时间一样,则将过期时间设置为10年
    } else {
        item.Expired = time.Now().Add(timeout)
    }
    item.Lastaccess = time.Now()
    data, err := GobEncode(item)  // 编码
    if err != nil {
        return err
    }
    return FilePutContents(fc.getCacheFileName(key), data)
}

存储Cache,传入键、值、过期时间,gob编码,以键值为索引保存到对应文件中

// Delete file cache value.
func (fc *FileCache) Delete(key string) error {
    filename := fc.getCacheFileName(key)
    if ok, _ := exists(filename); ok {
        return os.Remove(filename)
    }
    return nil
}

删除cache值,底层实现如果存在文件就直接删除文件

// Incr will increase cached int value.
// fc value is saving forever unless Delete.
func (fc *FileCache) Incr(key string) error {
    data := fc.Get(key)
    var incr int
    if reflect.TypeOf(data).Name() != "int" {
        incr = 0
    } else {
        incr = data.(int) + 1
    }
    fc.Put(key, incr, FileCacheEmbedExpiry)
    return nil
}

对key的cache值加1,修改并保存到文件里

// Decr will decrease cached int value.
func (fc *FileCache) Decr(key string) error {
    data := fc.Get(key)
    var decr int
    if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 {
        decr = 0
    } else {
        decr = data.(int) - 1
    }
    fc.Put(key, decr, FileCacheEmbedExpiry)
    return nil
}

对key的cache值减1,修改并保存到文件里

// IsExist check value is exist.
func (fc *FileCache) IsExist(key string) bool {
    ret, _ := exists(fc.getCacheFileName(key))
    return ret
}
// check file exist.
func exists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil {
        return true, nil
    }
    if os.IsNotExist(err) {
        return false, nil
    }
    return false, err
}

检查是否key,返回bool

// FilePutContents Put bytes to file. 
// if non-exist, create this file.
func FilePutContents(filename string, content []byte) error {
    fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
    if err != nil {
        return err
    }
    defer fp.Close()
    _, err = fp.Write(content)
    return err
}

这个函数是实现将[]byte数据放入到文件中
如果文件不存在,则创建,之后将数据写入到文件中

// GobEncode Gob encodes file cache item.
func GobEncode(data interface{}) ([]byte, error) {
    buf := bytes.NewBuffer(nil)
    enc := gob.NewEncoder(buf)
    err := enc.Encode(data)
    if err != nil {
        return nil, err
    }
    return buf.Bytes(), err
}

这个函数实现将数据进行Gob编码。

// GobDecode Gob decodes file cache item.
func GobDecode(data []byte, to *FileCacheItem) error {
    buf := bytes.NewBuffer(data)
    dec := gob.NewDecoder(buf)
    return dec.Decode(&to)
}

Gob实现将文件进行解码

现在分析第三个文件memory

var (
    // DefaultEvery means the clock time of recycling the expired cache items in memory.
    DefaultEvery = 60 // 1 minute
)

默认回收内存中过期缓存项的时钟时间。

// MemoryItem store memory cache item.
type MemoryItem struct {
    val         interface{}              // 值
    createdTime time.Time       // 创建时间
    lifespan    time.Duration      // 生存周期
}
func (mi *MemoryItem) isExpire() bool {
    // 0 means forever
    if mi.lifespan == 0 {
        return false
    }
    return time.Now().Sub(mi.createdTime) > mi.lifespan
}

判断是否过期

// MemoryCache is Memory cache adapter.
// it contains a RW locker for safe map storage.
type MemoryCache struct {
    sync.RWMutex              // 读写锁
    dur   time.Duration         // 过期时间 
    items map[string]*MemoryItem    // 缓存内容
    Every int // run an expiration check Every clock time,每一段时间运行一次过期检查
}

MemoryCache is Memory cache 适配器.
使用读写锁以保证了并发安全操作

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

创建一个memoryCache

// Get cache from memory.
// if non-existed or expired, return 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
}

从memory中获取一个值

// GetMulti gets caches from memory.
// if non-existed or expired, return nil.
func (bc *MemoryCache) GetMulti(names []string) []interface{} {
    var rc []interface{}
    for _, name := range names {
        rc = append(rc, bc.Get(name))
    }
    return rc
}

从memory中获取一组值

// Put cache to memory.
// if lifespan is 0, it will be forever till restart.
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
}

写入一条数据到memory cache中

// Delete cache in memory.
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
}

从memory cache中删除一个值

// Incr increase cache counter in memory.
// it supports 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
}

对key对应的值加一

// Decr decrease counter in memory.
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
}

对key的值减1

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

检查cache缓存是否存在

// ClearAll will delete all cache in memory.
func (bc *MemoryCache) ClearAll() error {
    bc.Lock()
    defer bc.Unlock()
    bc.items = make(map[string]*MemoryItem)
    return nil
}

清空memory所有cache

// StartAndGC start memory cache. it will check expiration in every clock time.
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
}

// check expiration.  检查过期时间
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 returns key list which are expired.返回过期key
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 removes all the items which key in keys.  根据key切片,清除key对应的cache
func (bc *MemoryCache) clearItems(keys []string) {
    bc.Lock()
    defer bc.Unlock()
    for _, key := range keys {
        delete(bc.items, key)
    }
}

func init() {
    Register("memory", NewMemoryCache)
}
上一篇 下一篇

猜你喜欢

热点阅读