package memcache import ( "fmt" "leafstalk/covenant/monitor" "leafstalk/module" "leafstalk/otherutils" "reflect" "strconv" "time" cach2 "github.com/patrickmn/go-cache" ) type GenericCache[T interface{}] struct { AllPlayers *cach2.Cache tokens otherutils.RoutineTokens loadFilter otherutils.FilterInt64 skeleton *module.Skeleton name string Load func(id int64) (*T, error) New func(playerId int64) *T PrePocess func(*T) } func NewCache[T interface{}](skeleton *module.Skeleton) *GenericCache[T] { c := new(GenericCache[T]) c.skeleton = skeleton var tmp *T val := reflect.TypeOf(tmp) if val != nil && val.Kind() == reflect.Ptr { c.name = val.Elem().Name() } return c } type callBackFun[T interface{}] struct { fprocess func(p *T) fErrProcess func(playerId int64, errorCode int) } func (c *GenericCache[T]) Init(load func(id int64) (*T, error), newObj func(playerId int64) *T, preProcess func(*T)) { c.AllPlayers = cach2.New(30*time.Minute, 10*time.Minute) c.tokens.Init(10) c.Load = load c.New = newObj c.PrePocess = preProcess } func (c *GenericCache[T]) GetCacheData(playerID int64) *T { k := strconv.FormatInt(playerID, 10) if v, ok := c.AllPlayers.Get(k); ok { if v2, ok := v.(*T); ok { return v2 } } return nil } func (c *GenericCache[T]) SetCacheData(playerID int64, v *T) { k2 := strconv.FormatInt(playerID, 10) c.AllPlayers.SetDefault(k2, v) } func (c *GenericCache[T]) LoadAndProcess(playerId int64, fprocess func(p *T), fErrProcess func(playerId int64, errorCode int)) { //找到玩家 st := time.Now() p := c.GetCacheData(playerId) if p != nil { c.PrePocess(p) c.SetCacheData(playerId, p) fprocess(p) monitor.GoLoadTimeoutWarn(fmt.Sprintf("%s.LoadAndProcess", c.name), playerId, st) return } //正在加载 if c.loadFilter.IsExist(playerId) { t := new(callBackFun[T]) t.fprocess = fprocess t.fErrProcess = fErrProcess c.loadFilter.AppendTask(playerId, t) return } c.loadFilter.Add(playerId) var err error c.skeleton.Go(func() { //协程加载 c.tokens.Get() defer c.tokens.Release() p, err = c.Load(playerId) }, func() { defer func() { s := c.loadFilter.Remove(playerId) for _, v := range s { cft := v.(*callBackFun[T]) if err != nil { cft.fErrProcess(playerId, 11) } else { cft.fprocess(p) } } monitor.GoLoadTimeoutWarn(fmt.Sprintf("%s.LoadAndProcess1", c.name), playerId, st) }() if err != nil { fErrProcess(playerId, 11) return } if IsNil(p) || p == nil { p = c.New(playerId) } c.PrePocess(p) c.SetCacheData(playerId, p) fprocess(p) }) } func IsNil(i interface{}) bool { vi := reflect.ValueOf(i) if vi.Kind() == reflect.Ptr { return vi.IsNil() } return false } func (c *GenericCache[T]) LoadMultiAndProcess(playerId1 int64, playerId2 int64, fprocess func(p1 *T, p2 *T), fErrProcess func(errorCode int)) { c.LoadAndProcess(playerId1, func(p1 *T) { c.LoadAndProcess(playerId2, func(p2 *T) { fprocess(p1, p2) }, func(playerId int64, errorCode int) { fErrProcess(errorCode) }) }, func(playerId int64, errorCode int) { fErrProcess(errorCode) }) }