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)
}
看完整体实现,我们发现这是一个项目还是蛮简单的。