groupcache 简介
在软件系统中使用缓存,可以降低系统响应时间,提高用户体验,降低某些系统模块的压力.
groupcache是一款开源的缓存组件.与memcache与redis不同的时,groupcache不需要单独的部署,可以作为你程序的一个库来使用. 这样方便我们开发的程序部署.
groupcache使用流程介绍
- NewGroup创建一个group实例,
// 创建一个Group实例,
// 第一个参数是group名字.
// 第二个参数是缓冲区大小.
// 第三个参数是回调函数.
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
return newGroup(name, cacheBytes, getter, nil)
}
// If peers is nil, the peerPicker is called via a sync.Once to initialize it.
func newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group {
if getter == nil {
panic("nil Getter")
}
mu.Lock()
defer mu.Unlock()
initPeerServerOnce.Do(callInitPeerServer)
if _, dup := groups[name]; dup {
panic("duplicate registration of group " + name)
}
g := &Group{
name: name,
getter: getter,
peers: peers,
cacheBytes: cacheBytes,
loadGroup: &singleflight.Group{},
}
if fn := newGroupHook; fn != nil {
fn(g)
}
groups[name] = g
return g
}
- 在创建Group实例的过程中,传入了一个回调函数,通过这个回到函数,将需要缓存的数据写入到cache中.
- 后边就可以通过Group提供的Get方法,按照key值,获取缓存数据.
下面来看看Group的Get方法.
func (g *Group) Get(ctx Context, key string, dest Sink) error {
// 如果注册了peer, 则下边的函数生效,否则无效.
g.peersOnce.Do(g.initPeers)
// Stats的Gets 自增1
// 统计分析使用
g.Stats.Gets.Add(1)
// dest不能为空,否则报错.
if dest == nil {
return errors.New("groupcache: nil dest Sink")
}
// 根据key值在cache中查找缓存的值.
value, cacheHit := g.lookupCache(key)
if cacheHit {
g.Stats.CacheHits.Add(1)
return setSinkView(dest, value)
}
// Optimization to avoid double unmarshalling or copying: keep
// track of whether the dest was already populated. One caller
// (if local) will set this; the losers will not. The common
// case will likely be one caller.
destPopulated := false
value, destPopulated, err := g.load(ctx, key, dest)
if err != nil {
return err
}
if destPopulated {
return nil
}
return setSinkView(dest, value)
}
- 上边的Get方法第一次调用时,其中的g.lookupCache(key)会返回false,主要原因是,缓冲区还没有写入任何有效值.
- Get的时候,会执行里面的g.load(ctx, key, dest)函数. 而load会调用NewGroup传入的第三个参数,即回调函数.
func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) {
err := g.getter.Get(ctx, key, dest)
if err != nil {
return ByteView{}, err
}
return dest.view()
}
- 上边的g.getter.Get(ctx,key,dest)即是NewGroup传入的回调函数.
- 所以,可以将需要缓存的信息在创建Group实例的时候在回调函数中进行初始化.
- 在回到函数中,通过实现了Sink的实例向Group的缓冲区中写入数据.Sink接口信息如下:
type Sink interface {
// SetString sets the value to s.
SetString(s string) error
// SetBytes sets the value to the contents of v.
// The caller retains ownership of v.
SetBytes(v []byte) error
// SetProto sets the value to the encoded version of m.
// The caller retains ownership of m.
SetProto(m proto.Message) error
// view returns a frozen view of the bytes for caching.
view() (ByteView, error)
}
实现了Sink接口的类如下:
- stringSink
- byteViewSink
- protoSink
- allocBytesSink
- truncBytesSink
上边几个类都是私有的类,不允许客户端直接创建实例,下边几个类的实例分别可以通过如下几个函数来创建: - StringSink(sp *string) Sink
- ByteViewSink(dst *ByteView) Sink
- ProtoSink(m proto.Message) Sink
- AllocatingByteSliceSink(dst *[]byte) Sink
- TruncatingByteSliceSink(dst *[]byte) Sink
下面来回顾一下上边的回调函数写入缓存信息的流程
- 创建实现Sink接口的类实例
- 在回调函数中使用第一步中创建出来的实例提供的方法将数据写入cache中
- 等到客户端使用Group实例来Get缓存时,如果是第一次调用,则执行回调函数,然后返回回调函数中写入的缓存信息,如果不是第一次调用,则直接读取缓存返回.
总结
- groupcache可以当做一个类库来使用.这样很方便我们在项目中灵活的使用.
- groupcache的缓存采用LRU的方式实现,这样大大提高高频缓存的查询效率.
- 可以将回调函数中传入channel变量,这样可以实现缓存数据的更新.原因是回调函数只能执行一次,所以,通过channel可以实现线程之间的方便通信.
- groupcache获取方式
go get github.com/golang/groupcache
温馨提示
以上内容,纯属个人愚见,有高人看出漏洞,请指点,以免瑕疵文章,误人子弟,感激不尽.
e-mail: hzwy23@163.com
来源:CSDN
作者:hzwy23
链接:https://blog.csdn.net/hzwy23/article/details/53294589