跳到主要内容

缓存组件

概要

缓存组件组要包括本地缓存和分布式缓存,两个组件都实现以下接口

type Cache interface {
Set(ctx context.Context, key string, val interface{}, expiration time.Duration) error
Get(ctx context.Context, key string, val interface{}) error
MultiSet(ctx context.Context, valMap map[string]interface{}, expiration time.Duration) error
MultiGet(ctx context.Context, keys []string, valueMap interface{}) error
Del(ctx context.Context, keys ...string) error
SetCacheWithNotFound(ctx context.Context, key string) error
}

本地缓存使用

内部主要使用 ristretto

初始化

func NewMemoryCache(keyPrefix string, encoding encoding.Encoding) Cache {
config := &ristretto.Config{
NumCounters: 1e7, // number of keys to track frequency of (10M).
MaxCost: 1 << 30, // maximum cost of cache (1GB).
BufferItems: 64, // number of keys per Get buffer.
}
store, _ := ristretto.NewCache(config)
return &memoryCache{
client: store,
KeyPrefix: keyPrefix,
encoding: encoding,
}
}

Example

  store := NewMemoryCache("memory-unit-test", encoding.JSONEncoding{})
ctx := context.Background()
var gotVal string
store.Set(ctx, "test-get-key", "test", 3600)
store.Get(ctx, "test-get-key", &gotVal)

分布式缓存使用

内部主要使用 redis

初始化

func NewRedisCache(client *redis.Client, keyPrefix string, encoding encoding.Encoding, newObject func() interface{}) Cache {
return &redisCache{
client: client,
KeyPrefix: keyPrefix,
encoding: encoding,
newObject: newObject,
}
}

Example

  ...
// 获取redis客户端
redisClient := redis.GetRedisClient()
// 实例化redis cache
// 指定编码格式为 json
cache := NewRedisCache(redisClient, "test-key", encoding.JSONEncoding{}, func() interface{} {
return new(int64)
})
...

如何在业务中使用

可以参考以下案例,代码使用工具生成

# 生成命令
eagle cache add UserCache
package cache

//go:generate mockgen -source=user_cache.go -destination=../../internal/mock/user_cache_mock.go -package mock

import (
"context"
"fmt"
"time"

"github.com/spf13/cast"

"github.com/go-eagle/eagle/pkg/cache"
"github.com/go-eagle/eagle/pkg/encoding"
"github.com/go-eagle/eagle/pkg/log"
"github.com/go-redis/redis/v8"

"github.com/go-microservice/user-service/internal/model"
)

const (
// PrefixUserCacheKey cache prefix
PrefixUserCacheKey = "user:%d"
)

type UserCache interface {
SetUserCache(ctx context.Context, id int64, data *model.UserModel, duration time.Duration) error
GetUserCache(ctx context.Context, id int64) (ret *model.UserModel, err error)
MultiGetUserCache(ctx context.Context, ids []int64) (map[string]*model.UserModel, error)
MultiSetUserCache(ctx context.Context, data []*model.UserModel, duration time.Duration) error
DelUserCache(ctx context.Context, id int64) error
SetCacheWithNotFound(ctx context.Context, id int64) error
}

// userCache define a cache struct
type userCache struct {
cache cache.Cache
}

// NewUserCache new a cache
func NewUserCache(rdb *redis.Client) UserCache {
jsonEncoding := encoding.JSONEncoding{}
cachePrefix := ""
return &userCache{
cache: cache.NewRedisCache(rdb, cachePrefix, jsonEncoding, func() interface{} {
return &model.UserModel{}
}),
}
}

// GetUserCacheKey get cache key
func (c *userCache) GetUserCacheKey(id int64) string {
return fmt.Sprintf(PrefixUserCacheKey, id)
}

// SetUserCache write to cache
func (c *userCache) SetUserCache(ctx context.Context, id int64, data *model.UserModel, duration time.Duration) error {
if data == nil || id == 0 {
return nil
}
cacheKey := c.GetUserCacheKey(id)
err := c.cache.Set(ctx, cacheKey, data, duration)
if err != nil {
return err
}
return nil
}

// GetUserCache 获取cache
func (c *userCache) GetUserCache(ctx context.Context, id int64) (ret *model.UserModel, err error) {
var data *model.UserModel
cacheKey := c.GetUserCacheKey(id)
err = c.cache.Get(ctx, cacheKey, &data)
if err != nil {
log.WithContext(ctx).Warnf("get err from redis, err: %+v", err)
return nil, err
}
return data, nil
}

// MultiGetUserCache 批量获取cache
func (c *userCache) MultiGetUserCache(ctx context.Context, ids []int64) (map[string]*model.UserModel, error) {
var keys []string
for _, v := range ids {
cacheKey := c.GetUserCacheKey(v)
keys = append(keys, cacheKey)
}

// NOTE: 需要在这里make实例化,如果在返回参数里直接定义会报 nil map
itemMap := make(map[string]*model.UserModel)
err := c.cache.MultiGet(ctx, keys, itemMap)
if err != nil {
return nil, err
}

retMap := make(map[string]*model.UserModel)
for _, v := range ids {
val, ok := itemMap[c.GetUserCacheKey(v)]
if ok {
retMap[cast.ToString(v)] = val
}
}
return retMap, nil
}

// MultiSetUserCache 批量设置cache
func (c *userCache) MultiSetUserCache(ctx context.Context, data []*model.UserModel, duration time.Duration) error {
valMap := make(map[string]interface{})
for _, v := range data {
cacheKey := c.GetUserCacheKey(v.ID)
valMap[cacheKey] = v
}

err := c.cache.MultiSet(ctx, valMap, duration)
if err != nil {
return err
}
return nil
}

// DelUserCache 删除cache
func (c *userCache) DelUserCache(ctx context.Context, id int64) error {
cacheKey := c.GetUserCacheKey(id)
err := c.cache.Del(ctx, cacheKey)
if err != nil {
return err
}
return nil
}

// DelUserCache set empty cache
func (c *userCache) SetCacheWithNotFound(ctx context.Context, id int64) error {
cacheKey := c.GetUserCacheKey(id)
err := c.cache.SetCacheWithNotFound(ctx, cacheKey)
if err != nil {
return err
}
return nil
}

来源: https://github.com/go-microservice/user-service/blob/main/internal/cache/user_cache.go

Reference