groupcache源码分析

August 21, 2019

周末阅读groupcache的源码,比较简单,也总结一下。

定位

groupcache虽然跟memcache是出自同一作者之手,但是定位相差挺大。memcached(简称mc)是一个独立的nosql存储服务,后台服务必须按照mc协议与之通信来实现数据缓存功能。而groupcache只是一个简单的缓存lib,定位类似于leveldb,后台服务必须集成到代码中来实现缓存的能力。

代码及功能简介

  • LRU支持LRU的cache实现,与传统的LRU实现没有大的差别。核心数据结构:
[go]
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
}

// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
type  Key  interface{}

type  entry  struct  {
  key Key
  value interface{}
}

为保证Add/Get/Remove性能,底层使用map结构来存储。可以通过key快速定位。

注意map中的value并不是实际数据,而是list.Element指针,list.Element封装了key、value。引入list的目的是为了实现Least recently used,跟memcached等实现在struct中加入双链表的结构,目的是一样的。

  • Consistent hash

    一致性hash实现,用作HTTPPool中的peers管理。注意有使用副本replicas参数控制虚拟节点数大小。

  • Single flight

    用于控制key的并发访问个数。使用memcache等缓存的毛病就是当cache中的key过期后,逻辑层短时间内可能有大量的cache miss,并发访问导致底层出现毛刺、雪崩。Single Fligth的目的就是为了避免这种场景,同一时间只允许一个到peers的访问,其他请求被block。

[go]
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
    mu sync.Mutex       // protects m
    m  map[string]*call // lazily initialized
}

// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
    g.mu.Lock()
    if g.m == nil {
        g.m = make(map[string]*call)
    }
    // 对应key已有请求flight中,等待返回
    if c, ok := g.m[key]; ok {
        g.mu.Unlock()
        c.wg.Wait()
        return c.val, c.err
    }
    c := new(call)
    c.wg.Add(1)
    g.m[key] = c
    g.mu.Unlock()

    c.val, c.err = fn()
    c.wg.Done()

    g.mu.Lock()
    delete(g.m, key)
    g.mu.Unlock()

    return c.val, c.err
}
  • Peer

管理peers节点,groupcache从远程load数据时,需要通过PickPeer方法获取一个peer对象。

 

To be continue…

See all postsSee all posts