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源码解析