从我们之前使用的代码可以看出,我们要使用 GroupCache 首先必须要使用 NewGroup 接口来创建一个 Group 结构,Group 结构定义在 groupcache.go 文件中,具体代码如下:
// A Group is a cache namespace and associated data loaded spread over
// a group of 1 or more machines.
type Group struct {
name string
getter Getter
peersOnce sync.Once
peers PeerPicker
cacheBytes int64 // limit for sum of mainCache and hotCache size
// mainCache is a cache of the keys for which this process
// (amongst its peers) is authoritative. That is, this cache
// contains keys which consistent hash on to this process's
// peer number.
mainCache cache
// hotCache contains keys/values for which this peer is not
// authoritative (otherwise they would be in mainCache), but
// are popular enough to warrant mirroring in this process to
// avoid going over the network to fetch from a peer. Having
// a hotCache avoids network hotspotting, where a peer's
// network card could become the bottleneck on a popular key.
// This cache is used sparingly to maximize the total number
// of key/value pairs that can be stored globally.
hotCache cache
// loadGroup ensures that each key is only fetched once
// (either locally or remotely), regardless of the number of
// concurrent callers.
loadGroup flightGroup
_ int32 // force Stats to be 8-byte aligned on 32-bit platforms
// Stats are statistics on the group.
Stats Stats
}
所有的字段解释如下:
字段 | 说明 |
---|---|
name | 创建的 Group 名 |
getter | 从 Group 中获取数据的接口实现 |
peersOnce | peersOnce 用于单次初始化集群中节点的函数 |
peers | peers 用于判断查找的 key 是否属于当前节点。如果当前 key 属于当前节点,那么返回 nil 和 false,如果不属于当前节点,那么返回 true |
cacheBytes | 单个 Group 能够缓存的键的数目 |
mainCache | 当前节点中所有缓存的键值数据 |
hotCache | 存储了不属于当前节点,但是比较热的数据,以防止每次热数据都要从别的节点获取,需要占用网络资源,影响效率 |
loadGroup | 用于保证每一个键都只会被加载一次 |
Stats | 对 Group 的统计 |
接下来,我们来看 NewGroup 接口,具体代码如下:
// NewGroup creates a coordinated group-aware Getter from a Getter.
//
// The returned Getter tries (but does not guarantee) to run only one
// Get call at once for a given key across an entire set of peer
// processes. Concurrent callers both in the local process and in
// other processes receive copies of the answer once the original Get
// completes.
//
// The group name must be unique for each getter.
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
return newGroup(name, cacheBytes, getter, nil)
}
在 Golang 中,NewXXX 就是构造函数,这里我们使用 NewGroup 传入缓存的名字、缓存的大小以及获取数据的接口创建一个缓存对象。在 NewGroup 里面,调用了 newGroup,具体代码如下:
// 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
}
在 newGroup 中,首先调用全局的 读写锁 进行加锁,最后,使用 defer 进行解锁,接着,我们使用 sync.Once 对象来进行接待你的初始化工作,并查看当前的 Group Name 是否已经在全局的 groups 中了,如果已经存在了,则直接 panic,否则,就创建 Group 对象。
最后,如果有创建 Group 的回调函数,那么直接调用,并且将创建好的 group 对象存放到全局的 groups 的 map 中。groups 的结构定义如下:
groups = make(map[string]*Group)
就是一个 group 名和 Group 对象的一个 map,还提供了一个全局的 GetGroup 接口,具体定义如下:
// GetGroup returns the named group previously created with NewGroup, or
// nil if there's no such group.
func GetGroup(name string) *Group {
mu.RLock()
g := groups[name]
mu.RUnlock()
return g
}
首先,使用读加锁,然后从 map 中获取数据,最后解锁,并返回获取的 Group 对象即可。