package main import ( "fmt" "time" "mongo.games.com/goserver/core" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/container/balancequeue" "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 缓存的无效玩家最大数量 InvalidPlayerCacheMax int = 100000 // ListNumber 平衡队列分组数量 ListNumber = 300 ) func init() { module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0) } 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), } type PlayerCacheItem struct { *model.PlayerData } func (p *PlayerCacheItem) BalanceQueueHandler() { // 并没有对缓存数据做什么操作,只是释放缓存 PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p) } type PlayerCacheMgr struct { *balancequeue.BalanceQueue playerMap map[int32]*PlayerCacheItem // snid; 玩家信息缓存 playerCbs map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法 playerInvalidIds map[int32]int64 // snid; 防止频繁访问数据库 playerWaitClr []*PlayerCacheItem // 根据DbSaver缓冲失效策略释放玩家数据 } // Get 获取玩家数据 // plt 平台 // snid 玩家id // cb 回掉方法; playerInfo 玩家数据; isFindDB 是否查了数据库; isNew 是否新创建的玩家 // createIfNotExist 玩家不存在是否创建 func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(playerInfo *PlayerCacheItem, isFindDB bool, isNew 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 { if len(replays) != len(internal.GetPlayerLoads()) { su = false } } if !su { c.cacheInvalidPlayerId(snid) for _, v := range c.playerCbs[snid] { v(nil, true, false) } delete(c.playerCbs, snid) return } 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.Push(item) } for _, v := range c.playerCbs[snid] { v(item, true, isNew) } delete(c.playerCbs, snid) })).StartByExecutor(fmt.Sprintf("Player%v", snid)) } //func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) { // isDB := false // count := len(snid) // result := make([]*PlayerCacheItem, 0, count) // innerCb := func(item *PlayerCacheItem, isFindDB, isNew bool) { // if item != nil { // result = append(result, item) // } // if isFindDB { // isDB = true // } // count-- // if count == 0 { // cb(result, isDB) // } // } // 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.BalanceQueue = balancequeue.New(ListNumber) } func (c *PlayerCacheMgr) Update() { c.BalanceQueue.Update() for _, p := range c.playerWaitClr { delete(c.playerMap, p.SnId) c.Pop(p) } c.playerWaitClr = c.playerWaitClr[0:0] } func (c *PlayerCacheMgr) Shutdown() { module.UnregisteModule(c) }