group 简介
在软件系统中使用缓存,可以降低系统响应时间,提高用户体验,降低某些系统模块的压力.
group是一款开源的缓存组件.与mem与redis不同的时,groupcache不需要单独的部署,可以作为你程序的一个库来使用. 这样方便我们开发的程序部署.
本篇主要解析groupcache中的关键部分, lru的定义以及如何做到同一个key只加载一次。
缓存填充以及加载抑制的实现
loadsingleflight
callcallwg
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
Groupcall
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
Group.Domapmapkeycall
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
如果当前的key已经在请求加载的过程中,那么解除上一步定义的冲突锁,并等待已经存在的加载请求结束后返回。
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
callmapcall.wg
c := new(call) c.wg.Add(1) g.m[key] = c g.mu.Unlock()
funccallwg.done
c.val, c.err = fn() c.wg.Done()
map
g.mu.Lock() delete(g.m, key) g.mu.Unlock()
sync.WaitGroup
cache(lru)
MaxEntriesOnEvictedllcache
type Cache struct {
// MaxEntries is the maximum number of cache entries before
// an item is evicted. Zero means no limit.
MaxEntries int
// OnEvicted optionally specifies a callback function to be
// executed when an entry is purged from the cache.
OnEvicted func(key Key, value interface{})
ll *list.List
cache map[interface{}]*list.Element
}
mapkeykeyindex
func (c *Cache) Add(key Key, value interface{}) {
if c.cache == nil {
c.cache = make(map[interface{}]*list.Element)
c.ll = list.New()
}
if ee, ok := c.cache[key]; ok {
c.ll.MoveToFront(ee)
ee.Value.(*entry).value = value
return
}
ele := c.ll.PushFront(&entry{key, value})
c.cache[key] = ele
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
c.RemoveOldest()
}
}
清理时会删除尾部元素, 这里就解释了为什么每次操作时会把元素提到首位。
func (c *Cache) RemoveOldest() {
if c.cache == nil {
return
}
ele := c.ll.Back()
if ele != nil {
c.removeElement(ele)
}
}
本文由 华域联盟 原创撰写:准格尔旗术交电子产品销售服务部 » golang中cache组件的使用及groupcache源码解析