我跟着极客兔兔的教程实现了分布式缓存,该系列文章是对实现过程的总结。
详细实现教程:7天用Go从零实现分布式缓存GeeCache
这篇文章会使用互斥锁:Mutex 来保证缓存并发读写时的安全性,然后在此基础上实现单机的缓存。
my-cache2\byteValue.go
type BytesValue struct {
Bytes []byte
}
func (v BytesValue) Len() int {
return len(v.Bytes)
}
func (v BytesValue) String() string {
return string(v.Bytes)
}
func (v BytesValue) ByteSlice() []byte {
return cloneBytes(v.Bytes)
}
func cloneBytes(bytes []byte) []byte {
b := make([]byte, len(bytes))
copy(b, bytes)
return b
}
my-cache2/mycache/mainCache.go
type mainCache struct {
cache *evict.Cache
maxBytes uint64
mu sync.Mutex
}
func (c *mainCache) get(key string) (my_cache2.BytesValue, bool) {
c.mu.Lock()
defer c.mu.Unlock()
if c.cache == nil {
c.cache = evict.NewCache(c.maxBytes)
c.cache.DeleteExpired()
}
value, ok := c.cache.Get(key)
if !ok {
return my_cache2.BytesValue{}, false
}
return value.(my_cache2.BytesValue), true
}
func (c *mainCache) add(key string, value my_cache2.BytesValue, expire int64) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.cache == nil {
c.cache = evict.NewCache(c.maxBytes)
c.cache.DeleteExpired()
}
return c.cache.Add(key, value, expire)
}
my-cache2\mycache\group.go
type RntValue struct {
Bytes []byte
Expire int64
}
type getter interface {
get(key string) (RntValue, error)
}
type GetterFunc func(key string) (RntValue, error)
func (g GetterFunc) get(key string) (RntValue, error) {
return g(key)
}
func(key string) (RntValue, error)
类型的方法转换为接口类型。func test(){
var g getter = GetterFunc(func(key string) (RntValue, error) {
//code......
})
}
下面就是核心数据类型:Group
type Group struct {
name string
cache *mainCache
getter getter
}
var (
rw sync.RWMutex
groups map[string]*Group = make(map[string]*Group)
)
func NewGroup(name string, maxBytes uint64, getter getter) *Group {
rw.Lock()
defer rw.Unlock()
if getter == nil {
panic("the getter is not allowed to be nil")
}
group := &Group{
name: name,
getter: getter,
cache: &mainCache{maxBytes: maxBytes},
}
groups[name] = group
return group
}
func GetGroup(name string) (*Group, bool) {
rw.RLock()
defer rw.RUnlock()
group := groups[name]
if group != nil {
return group, true
} else {
return nil, false
}
}
func (g *Group) Get(key string) (my_cache2.BytesValue, error) {
bytesValue, ok := g.cache.get(key)
if ok {
log.Printf("[MyCache] %s is hit in cache\n", key)
return bytesValue, nil
}
return g.loadLocally(key)
}
func (g *Group) loadLocally(key string) (my_cache2.BytesValue, error) {
rntValue, err := g.getter.get(key)
if err != nil {
return my_cache2.BytesValue{}, nil
}
log.Printf("[Slow DB] %s is searched in DB", key)
value := my_cache2.BytesValue{Bytes: rntValue.Bytes}
err = g.syncToCache(key, value, rntValue.Expire)
return value, err
}
func (g *Group) syncToCache(key string, value my_cache2.BytesValue, expire int64) error {
return g.cache.add(key, value, expire)
}
至此,单机并发缓存模块已经完成了,下面是测试。
//模拟数据库
var db = map[string]string{
"jok": "545",
"klo": "323",
"los": "232",
}
func TestGroup(t *testing.T) {
mycache.NewGroup("group1", 2<<10, mycache.GetterFunc(func(key string) (mycache.RntValue, error) {
s := db[key]
if s == "" {
return mycache.RntValue{}, errors.New("key is not find in db")
}
return mycache.RntValue{
Bytes: []byte(s),
Expire: 2,
}, nil
}))
group, _ := mycache.GetGroup("group1")
fmt.Println(group.Get("jok"))
fmt.Println(group.Get("klo"))
fmt.Println(group.Get("jok"))
fmt.Println(group.Get("klo"))
time.Sleep(2 * time.Second)
}
测试结果:
=== RUN TestGroup
2022/11/14 00:42:48 [Slow DB] jok is searched in DB
545
2022/11/14 00:42:48 [Slow DB] klo is searched in DB
323
2022/11/14 00:42:48 [MyCache] jok is hit in cache
545
2022/11/14 00:42:48 [MyCache] klo is hit in cache
323
--- PASS: TestGroup (0.14s)
PASS