game_sync/worldsrv/playercache.go

220 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}