go-cache学习与源码分析

github.com/patrickmn/go-cache
go-cache是一款类似于memached 的key/value 缓存软件。它比较适用于单机执行的应用程序。go-cache实质上就是拥有过期时间并且线程安全的map,可以被多个goroutine安全访问。并别代码量也相对较少,今天我们一起对go-cache代码包进行学习。

我们这里先上用法说明

package main

import (
        "log"
        "time"

        "github.com/patrickmn/go-cache"
)


func main(){

        c := cache.New(30*time.Second, 10*time.Second)//new 一个cache,并且设置默认过期时间30s和清理周期10s。

        value, found := c.Get("test") //从cache中读取key 为test的value。
        if found {
                log.Println("found:", value)
        } else {
                log.Println("not found")
        }


        c.Set("test", "test value", cache.DefaultExpiration) //向cache中写入key/value数据,并且设置过期时间为默认,这里的默认就是我们在new cache时设置怼默认过期时间(30s)


        value, found := c.Get("test")//从cache中读取key 为test的value。显然我们在这里就能读取到数据了
        if found {
                log.Println("found:", value)
        } else {
                log.Println("not found")
        }


        time.Sleep(60*time.Second) //我们等待过期时间以后,并且达到清理周期时间
        log.Println("sleep 60s...")
        value, found = c.Get("test")//再次读取key is test的数据,显然此时我们读取不到
        if found {
                log.Println("found:", value)
        } else {
                log.Println("not found")
        }

}

看完了用例说明我们再看下具体是怎么实现的;

package cache

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

type Item struct {
	Object     interface{}
	Expiration int64
}

// Returns true if the item has expired.
func (item Item) Expired() bool {
	if item.Expiration == 0 {
		return false
	}
	return time.Now().UnixNano() > item.Expiration
}

const (
	// For use with functions that take an expiration time.
	NoExpiration time.Duration = -1
	// For use with functions that take an expiration time. Equivalent to
	// passing in the same expiration duration as was given to New() or
	// NewFrom() when the cache was created (e.g. 5 minutes.)
	DefaultExpiration time.Duration = 0
)

type Cache struct {
	*cache
	// If this is confusing, see the comment at the bottom of New()
}

type cache struct {
	defaultExpiration time.Duration
	items             map[string]Item
	mu                sync.RWMutex
	onEvicted         func(string, interface{})
	janitor           *janitor
}

// 
func (c *cache) Set(k string, x interface{}, d time.Duration) {
	// "Inlining" of set
	var e int64
	if d == DefaultExpiration {
		d = c.defaultExpiration
	}
	if d > 0 {
		e = time.Now().Add(d).UnixNano()
	}
	c.mu.Lock()
	c.items[k] = Item{
		Object:     x,
		Expiration: e,
	}
	// TODO: Calls to mu.Unlock are currently not deferred because defer
	// adds ~200 ns (as of go1.)
	c.mu.Unlock()
}
/*
    中间代码
*/
//定时清理cache 过期结构
type janitor struct {
	Interval time.Duration
	stop     chan bool
}
//设置一个循环时钟,执行定时清理cache 的操作
func (j *janitor) Run(c *cache) {
	ticker := time.NewTicker(j.Interval)
	for {
		select {
		case <-ticker.C:
			c.DeleteExpired()
		case <-j.stop:
			ticker.Stop()
			return
		}
	}
}
//向runtime 注册的通知Janitor close 
func stopJanitor(c *Cache) {
	c.janitor.stop <- true
}

func runJanitor(c *cache, ci time.Duration) {
	j := &janitor{
		Interval: ci,
		stop:     make(chan bool),
	}
	c.janitor = j
	go j.Run(c)
}

func newCache(de time.Duration, m map[string]Item) *cache {
	if de == 0 {
		de = -1
	}
	c := &cache{
		defaultExpiration: de,
		items:             m,
	}
	return c
}

//具体的cache实现,
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
	c := newCache(de, m) //构建cache
	C := &Cache{c}
	if ci > 0 {
		runJanitor(c, ci) //这里是启动过期清理goroutine
		runtime.SetFinalizer(C, stopJanitor) //如果cache结束,首先通知Janitor关闭
	}
	return C
}
//这里就是我们用例中new cache的方法了。
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
	items := make(map[string]Item)
	return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}
//与New 方法功能相同唯一不同的就是由cache creator设置items 
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache {
	return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}

看完整体实现,我们发现这是一个项目还是蛮简单的。

 

上一篇:JAVA Socket无参构造方法的使用


下一篇:(寒假开黑gym)2018 ACM-ICPC, Syrian Collegiate Programming Contest(爽题)