```
feat(player): 添加玩家断开连接时的安全保存机制 - 实现 SaveOnDisconnect 方法,确保玩家数据在断开连接时安全保存 - 添加并发控制防止重复保存操作,使用互斥锁和完成通道确保一次保存 - 在 socket 关闭事件中改为异步调用 SaveOnDisconnect 避免阻塞 - 添加 panic 恢复机制保护保存过程中的异常情况 refactor(login): 优化登录时的踢人逻辑和超时处理
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/google/uuid"
|
||||
csmap "github.com/mhmtszr/concurrent-swiss-map"
|
||||
)
|
||||
@@ -247,18 +248,64 @@ func (s *InfoService) Save(data model.PlayerInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
|
||||
_ = s.saveWithRetry(data, true)
|
||||
}
|
||||
|
||||
func (s *InfoService) SaveUntilSuccess(data model.PlayerInfo) {
|
||||
if cool.Config.ServerInfo.IsVip != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
attempt = 0
|
||||
backoff = time.Second
|
||||
maxBackoff = 30 * time.Second
|
||||
)
|
||||
for {
|
||||
attempt++
|
||||
fallback := attempt == 1 || attempt%10 == 0
|
||||
err := s.saveWithRetry(data, fallback)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if i == 2 {
|
||||
cool.Logger.Error(context.TODO(), "player save failed after retries, fallback to local file", data.UserID, err)
|
||||
s.saveToLocalFile(&data, err)
|
||||
return
|
||||
if attempt == 1 || attempt%10 == 0 {
|
||||
cool.Logger.Error(context.TODO(), "player save retrying until success", data.UserID, attempt, err)
|
||||
}
|
||||
halfBackoff := int(backoff / 2)
|
||||
if halfBackoff < 1 {
|
||||
halfBackoff = 1
|
||||
}
|
||||
jitter := time.Duration(grand.Intn(halfBackoff))
|
||||
sleepFor := backoff + jitter
|
||||
if sleepFor > maxBackoff {
|
||||
sleepFor = maxBackoff
|
||||
}
|
||||
time.Sleep(sleepFor)
|
||||
if backoff < maxBackoff {
|
||||
backoff *= 2
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InfoService) saveWithRetry(data model.PlayerInfo, fallback bool) error {
|
||||
var lastErr error
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
if fallback && lastErr != nil {
|
||||
cool.Logger.Error(context.TODO(), "player save failed after retries, fallback to local file", data.UserID, lastErr)
|
||||
s.saveToLocalFile(&data, lastErr)
|
||||
}
|
||||
return lastErr
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user