gin框架实践连载番外篇 | 3天打造专属Cache(Third day)

1,190 阅读3分钟

引言

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的完结篇了,希望大家能有一点收获,同进步

7、系列文章