package redis import ( "context" "fmt" "kpt-pasture/config" "math/rand" "time" "gitee.com/xuyiping_admin/pkg/xerr" "github.com/eko/gocache/cache" "github.com/eko/gocache/marshaler" "github.com/eko/gocache/metrics" "github.com/eko/gocache/store" redisv7 "github.com/go-redis/redis/v7" ) func init() { rand.Seed(time.Now().UnixNano()) } // Options redis 使用约束 type Options = store.Options // TTL redis key TTL func TTL(duration time.Duration) *Options { return &store.Options{ // 避免集中过期 Expiration: duration + time.Duration(rand.Int31n(100))*time.Second, } } // CacheObject 缓存对象 type CacheObject interface { CacheKey() string } // CacheStoreRedis Redis 缓存实例 // //go:generate mockgen -destination mock/redismock.go -package redismock kpt_event/service/redis CacheStoreRedis type CacheStoreRedis interface { Namespace() string SetNamespace(string) // FullKey 返回带命名空间的完整key FullKey(key string) string Client() *redisv7.Client Get(ctx context.Context, Obj CacheObject) (interface{}, error) BatchGet(ctx context.Context, objects []CacheObject) (map[string]interface{}, error) Set(ctx context.Context, Obj CacheObject, options *store.Options) error TTL(ctx context.Context, obj CacheObject) (time.Duration, error) Delete(ctx context.Context, keys ...interface{}) error } func NewCacheStoreRedis(cfg *config.AppConfig) CacheStoreRedis { return NewCacheStoreRedisEntry(cfg) } func NewCacheStoreRedisEntry(cfg *config.AppConfig) *CacheStoreRedisEntry { client := NewClientLatest(cfg) redisStore := store.NewRedis(client, nil) promMetrics := metrics.NewPrometheus(cfg.Name()) cacheManager := cache.NewMetric(promMetrics, cache.New(redisStore)) marshal := marshaler.New(cacheManager) return &CacheStoreRedisEntry{ client: client, cacheNamespace: config.Options().CacheNameSpace(), cacheMarshal: marshal, } } type CacheStoreRedisEntry struct { client *redisv7.Client cacheNamespace string cacheMarshal *marshaler.Marshaler } func (entry *CacheStoreRedisEntry) fullKey(key string) string { return fmt.Sprintf("%s:%s", entry.cacheNamespace, key) } func (entry *CacheStoreRedisEntry) FullKey(key string) string { return fmt.Sprintf("%s:%s", entry.cacheNamespace, key) } func (entry *CacheStoreRedisEntry) pureKey(key string) string { return key[len(entry.cacheNamespace):] } func (entry *CacheStoreRedisEntry) Namespace() string { return entry.cacheNamespace } func (entry *CacheStoreRedisEntry) SetNamespace(ns string) { entry.cacheNamespace = ns } func (entry *CacheStoreRedisEntry) Client() *redisv7.Client { return entry.client } func (entry *CacheStoreRedisEntry) BatchGet(ctx context.Context, objects []CacheObject) (map[string]interface{}, error) { result := make(map[string]interface{}) for _, obj := range objects { got, err := entry.Get(ctx, obj) if err != nil { return nil, xerr.WithMessage(err, obj.CacheKey()) } if got != nil { result[obj.CacheKey()] = got } } return result, nil } func (entry *CacheStoreRedisEntry) Get(ctx context.Context, obj CacheObject) (interface{}, error) { got, err := entry.cacheMarshal.Get(entry.fullKey(obj.CacheKey()), obj) if err != nil { if err == redisv7.Nil { return nil, nil } return nil, xerr.WithStack(err) } return got, nil } func (entry *CacheStoreRedisEntry) Set(ctx context.Context, obj CacheObject, options *store.Options) error { return entry.cacheMarshal.Set(entry.fullKey(obj.CacheKey()), obj, options) } func (entry *CacheStoreRedisEntry) TTL(ctx context.Context, obj CacheObject) (time.Duration, error) { return entry.client.TTL(entry.fullKey(obj.CacheKey())).Result() } func (entry *CacheStoreRedisEntry) Delete(ctx context.Context, keys ...interface{}) error { for _, key := range keys { switch keyItem := key.(type) { case string: if err := entry.cacheMarshal.Delete(entry.fullKey(keyItem)); err != nil { return xerr.WithStack(err) } case CacheObject: if err := entry.cacheMarshal.Delete(entry.fullKey(keyItem.CacheKey())); err != nil { return xerr.WithStack(err) } default: return xerr.New("invalid redis key type") } } return nil }