引言
- 接着上一篇的文章,我们这期实现用memcached做存储源
- 没有看过前二篇的同学请关注
- 第一天
- 第二天
- 采用memcached作者写的第三方驱动库github.com/bradfitz/go…
- 案例代码地址
1、定义store结构
import (
"fmt"
"log"
"time"
"github.com/18211167516/gocache"
"github.com/bradfitz/gomemcache/memcache"
"github.com/gogf/gf/util/gconv"
)
type MemcachedStore struct {
MeClient *memcache.Client
SizeKey string //计算key的数量的键名
}
var MemCachedName = "Memcached"
2、实例化store以及注册cache
func NewMemcached(addr string) *MemcachedStore {
client := memcache.New(addr)
err := client.Ping()
if err != nil {
log.Panic("Cache store Memcached:", err)
}
return &MemcachedStore{
MeClient: client,
SizeKey: "size",
}
}
func RegisterMemcached(addr string) {
gocache.Register(MemCachedName, NewMemcached(addr))
}
3、实现的方法
// GC 定期扫删除过期键
func (ms *MemcachedStore) GC() {
}
func (ms *MemcachedStore) GetStoreName() string {
return MemCachedName
}
func (ms *MemcachedStore) Get(key string) (interface{}, error) {
item, err := ms.MeClient.Get(key)
if err != nil {
return nil, err
} else {
return gconv.String(item.Value), err
}
}
func NewMemItem(key string, value interface{}, d int) *memcache.Item {
return &memcache.Item{Key: key, Value: gconv.Bytes(value), Expiration: int32(10)}
}
func (ms *MemcachedStore) Incr(key string, i int) error {
if _, err := ms.MeClient.Increment(key, uint64(i)); err != nil {
if err == memcache.ErrCacheMiss {
return ms.Forever(key, gconv.String(i))
} else {
return err
}
}
return nil
}
func (ms *MemcachedStore) Decr(key string, i int) error {
if _, err := ms.MeClient.Decrement(key, uint64(i)); err != nil {
if err == memcache.ErrCacheMiss {
return ms.Forever(key, gconv.String(i))
} else {
return err
}
}
return nil
}
func (ms *MemcachedStore) Set(key string, value interface{}, d int) error {
item := NewMemItem(key, value, d)
if err := ms.MeClient.Set(item); err != nil {
return err
} else {
return ms.Incr(ms.SizeKey, 1)
}
}
func (ms *MemcachedStore) Delete(key string) error {
if err := ms.MeClient.Delete(key); err != nil {
return err
} else {
return ms.Decr(ms.SizeKey, 1)
}
}
func (ms *MemcachedStore) Has(key string) error {
item, err := ms.MeClient.Get(key)
if err != nil {
return err
}
if item == nil {
return fmt.Errorf("Cache store Memcached key:%s not found", key)
}
return nil
}
func (ms *MemcachedStore) Forever(key string, value interface{}) error {
return ms.Set(key, value, 0)
}
func (ms *MemcachedStore) Clear() error {
return ms.MeClient.DeleteAll()
}
func (ms *MemcachedStore) Size() int {
value, err := ms.Get(ms.SizeKey)
if err != nil {
return 0
} else {
return gconv.Int(value) - 1
}
}
func (ms *MemcachedStore) GetTTl(key string) (time.Duration, error) {
if err := ms.Has(key); err != nil {
return 0, err
} else {
item, _ := ms.MeClient.Get(key)
return time.Duration(item.Expiration), nil
}
}
func (ms *MemcachedStore) IsExpire(key string) (bool, error) {
if err := ms.Has(key); err != nil {
return false, err
} else {
item, _ := ms.MeClient.Get(key)
return item.Expiration > 0, nil
}
}
4、遇到的问题
刚开始我以为memcached为很容易,实际上卡在了Size方法,memcached并没有提供类似的方法,有个stats类似redis的info能拿到系统信息,可惜客户端没有提供这个方法,只能自己实现
细心的同学应该发现了,我在set和delete的多了一步操作,同时深入一下分享一下为啥这么写
func (ms *MemcachedStore) Incr(key string, i int) error {
if _, err := ms.MeClient.Increment(key, uint64(i)); err != nil { // 第一行
if err == memcache.ErrCacheMiss { // 第二行
return ms.Forever(key, gconv.String(i)) // 第三行
} else {
return err
}
}
return nil
}
- memcached有个机制如果值不存在,incrment递增和递减是会返回cache miss的
- 第二行我们判断了错误是否是cachemiss,也就相当于get值类似
- 第三行我们永久存储专门记录size的key,同时还将int转成了string,如果不做这不操作会报错 memcache: client error: cannot increment or decrement non-numeric value
5、驱动库的用法
6、结尾
这算是3天打造专属Cache的完结篇了,希望大家能有一点收获,同进步