diff --git a/src/server/game/external.go b/src/server/game/external.go index 8cf8505a..8b4b1f98 100644 --- a/src/server/game/external.go +++ b/src/server/game/external.go @@ -150,6 +150,7 @@ func HandleClientReq(args []interface{}) { detail := &msg.ReqLogin{} proto.Unmarshal(buf, detail) accountInfo := db.GetAccountInfoFromDb(detail.UserName) + log.Debug("player %s start login", detail.UserName) defer func() { if r := recover(); r != nil { GoUtil.SendFeishuFatal(0, m.GetFunc(), fmt.Sprintf("username: %s, fatal: %s", detail.UserName, r)) @@ -177,6 +178,7 @@ func HandleClientReq(args []interface{}) { db.UpdateAccountInfoDeviceToDb(accountInfo) p, _ := internal.Agents.Load(a) if p != nil { + log.Debug("player %s login success", detail.UserName) p.(*Player).PlayMod.getBaseMod().DiviceId = detail.Device //加锁 p.(*Player).PushClientRes(ResLogin) p.(*Player).LoginBackData() diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index 72fa1acb..93c9e6bb 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -175,7 +175,16 @@ func ChampshipRankInfoHandler(data *msg.Msg) (interface{}, error) { func NotifyAllPlayerMsg(m *msg.Msg) { messageMgrData := getMessageData() - for PlayerId, node := range messageMgrData.PlayerList { + // 先复制 PlayerList,避免长时间持有锁 + messageMgrData.mu.Lock() + playerListCopy := make(map[int64]int, len(messageMgrData.PlayerList)) + for k, v := range messageMgrData.PlayerList { + playerListCopy[k] = v + } + messageMgrData.mu.Unlock() + + // 在锁外发送消息 + for PlayerId, node := range playerListCopy { m.To = int(PlayerId) SendMsgToNodeAsync(m, node) } @@ -197,7 +206,11 @@ func CatnipPartnerHandler(data *msg.Msg) (interface{}, error) { func ReplyPlayerMsgASync(m *msg.Msg, reply interface{}) (interface{}, error) { clone := m.Reply(reply) messageMgrData := getMessageData() - if node, ok := messageMgrData.PlayerList[int64(m.From)]; ok { + messageMgrData.mu.Lock() + node, ok := messageMgrData.PlayerList[int64(m.From)] + messageMgrData.mu.Unlock() + + if ok { SendMsgToNodeAsync(clone, node) } return nil, nil @@ -214,12 +227,13 @@ func ClusterSyncHandler(data *msg.Msg) (interface{}, error) { }) return true }) - // 发送暂存区消息 + // 发送暂存区消息(先复制再释放锁,避免长时间持有锁) messageMgrData := getMessageData() messageMgrData.mu.Lock() TempMessageList := messageMgrData.MessageList messageMgrData.MessageList = make(map[int64]*MessageList) - defer messageMgrData.mu.Unlock() + messageMgrData.mu.Unlock() // 立即释放锁,在锁外发送消息 + log.Debug("[Middleware] Cluster sync send temp message len: %d", len(TempMessageList)) for _, Message := range TempMessageList { for _, msgItem := range Message.Messages { @@ -233,25 +247,34 @@ func PlayerLoginHandler(data *msg.Msg) (interface{}, error) { // 关闭 Worker Pool node := data.Extra.(int) messageMgrData := getMessageData() + // 先更新 PlayerList(需要加锁) + messageMgrData.mu.Lock() messageMgrData.PlayerList[int64(data.From)] = node + messageMgrData.mu.Unlock() + log.Debug("[Middleware] Player login success player id: %v, node: %v", data.From, data.Extra.(int)) // 对玩家消息列表加锁 messages := getMessge(int64(data.From)) messages.mu.Lock() - defer messages.mu.Unlock() - // 发送离线消息 - len := len(messages.Messages) - for _, message := range messages.Messages { + // 复制消息列表,避免在锁内发送消息 + messagesToSend := make([]*msg.Msg, len(messages.Messages)) + copy(messagesToSend, messages.Messages) + messages.mu.Unlock() + + // 在锁外发送离线消息 + for _, message := range messagesToSend { SendMsgToNodeAsync(message, node) } - log.Debug("[Middleware] Player sync logout message player id: %v, len: %d", data.From, len) + log.Debug("[Middleware] Player sync logout message player id: %v, len: %d", data.From, len(messagesToSend)) ReplyPlayerMsgASync(data, nil) return nil, nil } func PlayerLogoutHandler(data *msg.Msg) (interface{}, error) { messageMgrData := getMessageData() + messageMgrData.mu.Lock() delete(messageMgrData.PlayerList, int64(data.From)) + messageMgrData.mu.Unlock() log.Debug("[Middleware] Player logout success player id: %v", data.From) return nil, nil } @@ -277,9 +300,15 @@ func CenterPlayerMsgHandler(data *msg.Msg) (interface{}, error) { // 遍历消息列表,发送消息给在线玩家 messages := getMessge(PlayerId) messages.mu.Lock() - defer messages.mu.Unlock() messages.Messages = append(messages.Messages, data) - if node, ok := messageMgrData.PlayerList[int64(PlayerId)]; ok { + messages.mu.Unlock() + + // 检查玩家是否在线(需要加锁) + messageMgrData.mu.Lock() + node, ok := messageMgrData.PlayerList[int64(PlayerId)] + messageMgrData.mu.Unlock() + + if ok { SendMsgToNodeAsync(data, node) } return nil, nil @@ -763,7 +792,8 @@ func saveMessage(m *msg.Msg) error { data := getMessageData() data.mu.Lock() defer data.mu.Unlock() - messages := getMessge(int64(m.To)) + // 使用不加锁的内部方法,避免死锁 + messages := getMessgeUnsafe(int64(m.To)) messages.mu.Lock() defer messages.mu.Unlock() messages.Messages = append(messages.Messages, m) @@ -778,10 +808,9 @@ func GetUserData(PlayerId int64, Key string) (*msg.Msg, error) { }) } -func getMessge(PlayerId int64) *MessageList { +// getMessgeUnsafe 获取消息列表(不加锁,调用者需要持有锁) +func getMessgeUnsafe(PlayerId int64) *MessageList { messageMgrData := getMessageData() - messageMgrData.mu.Lock() - defer messageMgrData.mu.Unlock() if _, ok := messageMgrData.MessageList[int64(PlayerId)]; !ok { messageMgrData.MessageList[int64(PlayerId)] = &MessageList{ Messages: []*msg.Msg{}, @@ -790,6 +819,14 @@ func getMessge(PlayerId int64) *MessageList { return messageMgrData.MessageList[int64(PlayerId)] } +// getMessge 获取消息列表(加锁版本) +func getMessge(PlayerId int64) *MessageList { + messageMgrData := getMessageData() + messageMgrData.mu.Lock() + defer messageMgrData.mu.Unlock() + return getMessgeUnsafe(PlayerId) +} + func deleteMessage(m *msg.Msg) error { if m == nil { return nil @@ -797,16 +834,25 @@ func deleteMessage(m *msg.Msg) error { messages := getMessge(int64(m.To)) messages.mu.Lock() defer messages.mu.Unlock() + + // 使用更安全的方式删除元素:找到索引后再删除,避免在range中修改切片 + foundIndex := -1 for i, msgItem := range messages.Messages { if msgItem == nil { continue } if msgItem.UniKey == m.UniKey { - // 删除消息 - messages.Messages = append(messages.Messages[:i], messages.Messages[i+1:]...) + foundIndex = i log.Debug("[Middleware] send message success; message: %v, player id: %v", msgItem, msgItem.From) break } } + + if foundIndex >= 0 { + // 删除消息:将后面的元素前移,避免内存泄漏 + copy(messages.Messages[foundIndex:], messages.Messages[foundIndex+1:]) + messages.Messages[len(messages.Messages)-1] = nil // 清除最后一个元素的引用 + messages.Messages = messages.Messages[:len(messages.Messages)-1] + } return nil }