package main import ( "fmt" "time" "mongo.games.com/goserver/core" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/module" "mongo.games.com/goserver/core/task" "mongo.games.com/game/model" "mongo.games.com/game/worldsrv/internal" ) const ( // InvalidPlayerCacheSec 数据不存在,60秒内不再查询 InvalidPlayerCacheSec int64 = 60 InvalidPlayerCacheMax int = 100000 ListNumber int32 = 300 ) func init() { module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0) //GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGIN, PlayerCacheMgrSingleton) //GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGOUT, PlayerCacheMgrSingleton) } var PlayerCacheMgrSingleton = &PlayerCacheMgr{ playerMap: make(map[int32]*PlayerCacheItem), playerCbs: make(map[int32][]func(*PlayerCacheItem, bool, bool)), playerInvalidIds: make(map[int32]int64), playerWaitClr: make([]*PlayerCacheItem, 0, 128), DbSaver: &DbSaver{ Tick: ListNumber, index: 0, init: false, list: make([]*SaverArray, ListNumber), queue: make([]*BalanceQueue, 10), pool: make(map[SaveTaskHandler]*SaverArray), }, } type PlayerCacheItem struct { *model.PlayerData } func (p *PlayerCacheItem) CanDel() bool { //return !p.isOnline && time.Now().Unix()-p.lastTs > int64(ListNumber) return true } func (p *PlayerCacheItem) Time2Save() { // 并没有对缓存数据做什么操作,只是释放缓存 if p.CanDel() { PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p) } } type PlayerCacheMgr struct { *DbSaver playerMap map[int32]*PlayerCacheItem // snid; 玩家信息缓存 playerCbs map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法 playerInvalidIds map[int32]int64 // snid; 防止频繁访问数据库 playerWaitClr []*PlayerCacheItem // 根据DbSaver缓冲失效策略释放玩家数据 } func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(*PlayerCacheItem, bool, bool), createIfNotExist bool) { // 1.玩家缓冲数据存在 if p, exist := c.playerMap[snid]; exist { cb(p, false, false) return } // 2.查询频率限制(限制数据库没有数据的情况) // 玩家不存在,避免打到db上 // 数据库查询失败或没有玩家信息就不要重复查了 if lastTs, exist := c.playerInvalidIds[snid]; exist { if time.Now().Unix()-lastTs < InvalidPlayerCacheSec { cb(nil, false, false) return } delete(c.playerInvalidIds, snid) } // 3.有正在查询中,等待查询结果 // 查询队列,如果已经有在查数据库了,就等待数据库查询结果 if cbs, exist := c.playerCbs[snid]; exist { cbs = append(cbs, cb) c.playerCbs[snid] = cbs return } c.playerCbs[snid] = []func(*PlayerCacheItem, bool, bool){cb} var isnew bool var replays []*internal.PlayerLoadReplay task.New(core.CoreObject(), task.CallableWrapper(func(o *basic.Object) interface{} { pi, flag := model.GetPlayerDataBySnId(plt, snid, true, createIfNotExist) isnew = flag for _, v := range internal.GetPlayerLoads() { replays = append(replays, v.Load(plt, snid, pi)) } return pi }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { su := true pi, ok := data.(*model.PlayerData) if !ok || pi == nil { su = false } if su { for _, v := range replays { if v == nil { continue } if v.Err != nil { su = false break } } } if !su { c.cacheInvalidPlayerId(snid) delete(c.playerCbs, snid) for _, cb := range c.playerCbs[snid] { cb(nil, true, false) } return } if len(replays) == len(internal.GetPlayerLoads()) { for k, v := range internal.GetPlayerLoads() { if v == nil || replays[k] == nil { continue } v.Callback(pi, replays[k]) } } // 查询成功,缓存数据,执行cb方法 item, ok := c.playerMap[snid] if !ok { item = &PlayerCacheItem{PlayerData: pi} c.playerMap[snid] = item c.RegisterDbSaverTask(item) } if cbs, exist := c.playerCbs[snid]; exist { delete(c.playerCbs, snid) for _, cb := range cbs { cb(item, true, isnew) } } })).StartByExecutor(fmt.Sprintf("Player%v", snid)) } func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) { isAsyn := false count := len(snid) result := make([]*PlayerCacheItem, 0, count) innerCb := func(item *PlayerCacheItem, asyn, isnew bool) { if item != nil { result = append(result, item) } if asyn { isAsyn = true } count-- if count == 0 { cb(result, isAsyn) } } for _, id := range snid { c.Get(plt, id, innerCb, false) } } func (c *PlayerCacheMgr) cacheInvalidPlayerId(snid int32) { if len(c.playerInvalidIds) >= InvalidPlayerCacheMax { for id := range c.playerInvalidIds { delete(c.playerInvalidIds, id) if len(c.playerInvalidIds) < InvalidPlayerCacheMax { break } } } c.playerInvalidIds[snid] = time.Now().Unix() } // UnCacheInvalidPlayerId 解除一次玩家数据查询频率限制 // 玩家数据不存在,限制查询频率,避免查询过于频繁 // 但是当玩家数据确认存在后,可以解除限制,提前允许查询到玩家数据 func (c *PlayerCacheMgr) UnCacheInvalidPlayerId(snid int32) { delete(c.playerInvalidIds, snid) } func (c *PlayerCacheMgr) ModuleName() string { return "PlayerCacheMgr" } func (c *PlayerCacheMgr) Init() { c.DbSaver.Init() } func (c *PlayerCacheMgr) Update() { // 执行Time2Save之后清除缓存 c.DbSaver.Update() for _, p := range c.playerWaitClr { delete(c.playerMap, p.SnId) c.UnregisteDbSaveTask(p) } c.playerWaitClr = c.playerWaitClr[0:0] } func (c *PlayerCacheMgr) Shutdown() { module.UnregisteModule(c) }