搜索
您的当前位置:首页正文

go实现一个本地缓存

来源:独旅网

本地缓存

一、为什么要使用本地缓存

二、如何设计一个本地缓存

基于不同的业务场景,我们可以选择设计一个符合自己业务场景的缓存。
对于我需要的而言,我需要是
1、能够定时从数据库中自动加载数据。
2、支持并发安全
3、支持过期淘汰
4、便于扩展、较为通用

三、本地缓存的简单实现

接口定义

// CacheInterface 提供对于缓存 get set del
type CacheInterface interface {
	SetVal(key string, response *Value) error
	GetVal(key string) *Value
	DelVal(key string)
	loadFunc(ctx context.Context, cache *ICache)
}
// cache 实现
//ICache
// cache 缓存
// callback 执行函数
// executeInterval 定期刷新时间
// key 唯一key
// params 请求参数
// reTry 执行异常重试次数
// reTryInterVal 执行异常重试间隔
// reSetCountDay 统计重置函数更新的次数,用来判断改数据的操作是否频繁 包含day、week、month 维度
// reSetCountWeek week 维度次数统计
// reSetCountMonth 月统计维度
type ICache struct {
	callback        CallBackFunc
	executeInterval time.Duration
	key             string
	params          map[string]interface{}
	reTry           int
	reTryInterval   time.Duration
	reSetCountDay   int
	reSetCountWeek  int
	reSetCountMonth int
}
//构造器
func NewCache() *ICache {
	return &ICache{}
}
// CallBackFunc 执行回调函数
type CallBackFunc func(map[string]interface{}) *Value

var ErrLargeKey = errors.New("the key is larger than 65535")
var maxKeyLen = 65535
var reTry = 3
var reTryInterval = time.Second * 10

func Set(request *SingleRequest) error {
	return NewCache().SetVal(request.Key, request.Value)
}

func (c *ICache) SetVal(key string, response *Value) error {
	if len(key) > maxKeyLen {
		return ErrLargeKey
	}
	setVal(key, response)
	return nil
}

func Get(key string) *Value {
	return getVal(key)
}

func Del(key string) {
	delVal(key)
	return
}

func (c *ICache) GetVal(key string) *Value {
	return getVal(key)
}

func (c *ICache) DelVal(key string) {
	delVal(key)
	return
}

// LoadFunc 提供加载
func LoadFunc(request *LoadRequest) {
	ctx := context.Background()
	if request.ReTry == 0 {
		request.ReTry = reTry
	}
	if request.ReTryInterval == 0 {
		request.ReTryInterval = reTryInterval
	}
	cache := &ICache{
		callback:        request.Callback,
		executeInterval: request.TTL,
		key:             request.Key,
		params:          request.CallBackParams,
		reTry:           request.ReTry,
		reTryInterval:   request.ReTryInterval,
		reSetCountDay:   request.ReSetCountDay,
		reSetCountWeek:  request.ReSetCountWeek,
		reSetCountMonth: request.ReSetCountMonth,
	}
	go cache.loadFunc(ctx, cache)
}

func (c *ICache) loadFunc(ctx context.Context, cache *ICache) {
	defer func() {
		if x := recover(); x != nil {
			log.Println("x--->", string(debug.Stack()))
		}
	}()
SyncLoop:
	for {
		res := cache.callback(cache.params)
	//	fmt.Println("totalCount-->", res.TotalCount, "one-->", res.ResultOne, "list-->", res.ResultList)
		temp := 3
		var err error
		for temp > 0 {
			res = cache.callback(cache.params)
			if res.error == nil {
				break
			}
			err = res.error
			time.Sleep(time.Second * 3)
			fmt.Println("每隔3秒重试一次:", cache.key)
			temp--
		}
		if temp == 0 && err != nil {
			ctx.Done()
			fmt.Println("重试失败,结束重试!!!", err.Error())
			break
		}
		if res.error != nil {
			log.Printf("查询失败")
			ctx.Done()
			break
		}
		if err := c.SetVal(cache.key, res); err != nil {
			ctx.Done()
			break
		}
		select {
		case <-time.After(cache.executeInterval):
			log.Default().Printf("%s", "下一次循环")
		case <-ctx.Done():
			log.Printf("协程处理失败")
			break SyncLoop
		}
	}
}

func setVal(key string, response *Value) {
	cacheLock.Lock()
	LocalCache[key] = response
	cacheLock.Unlock()
}

func getVal(key string) *Value {
	return LocalCache[key]
}

func delVal(key string) {
	cacheLock.Lock()
	delete(LocalCache, key)
	cacheLock.Unlock()
}

protol

// LoadRequest 缓存设置包结构
// CallBack 执行加载函数
// TTL 过期时间
// Key 缓存key
// CallBackParams callback 请求参数
// ReTry 重试次数
// ReTryInterval 重试时间间隔
// ReTryInterVal 执行异常重试间隔
// ReSetCountDay 统计重置函数更新的次数,用来判断改数据的操作是否频繁 包含day、week、month 维度
// ReSetCountWeek week 维度次数统计
// ReSetCountMonth 月统计维度
type LoadRequest struct {
	Callback        CallBackFunc
	TTL             time.Duration
	Key             string
	CallBackParams  map[string]interface{}
	ReTry           int
	ReTryInterval   time.Duration
	ReSetCountDay   int
	ReSetCountWeek  int
	ReSetCountMonth int
}

// SingleRequest 不需要定时加载,直接set 缓存
type SingleRequest struct {
	Key   string        `json:"key"`
	Value *Value         `json:"value"`
	TTL   time.Duration `json:"ttl"`
}

// Value 设置缓存结构体
type Value struct {
	TotalCount int64
	ResultList []map[string]interface{}
	ResultOne  map[string]interface{}
	error      error
}

test 测试

func TestSetAndGet(t *testing.T) {
	err := Set(&SingleRequest{Key: "key1", Value: &Value{TotalCount: 1, ResultList: nil, ResultOne: map[string]interface{}{"1": 1}, error: nil}})
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println("key1缓存设置成功")
	res := NewCache().GetVal("key1")
	fmt.Println("key1最终结果", res.TotalCount, "  ", res.ResultOne, "  ", res.ResultList)
}

func TestLoad(t *testing.T) {
	LoadFunc(&LoadRequest{
		Callback: NewPerson().QueryPersonInfo,
		Key:      "person",
	})
	res := Get("person")
	fmt.Println("person--------->",res)
	time.Sleep(1 * time.Second)
	res = Get("person")
	fmt.Println("person--------->",res)
}

type Person struct {
}

func NewPerson() *Person {
	return &Person{}
}
func (person *Person) QueryPersonInfo(params map[string]interface{}) *Value {
	//db.Query
	p := map[string]interface{}{"id": 1, "name": "zdm", "age": 18, "sex": "man"}
	return &Value{TotalCount: 1, ResultOne: p}
}

测试效果

因篇幅问题不能全部显示,请点此查看更多更全内容

Top