1.离线玩家数据加分布式锁操作

2.修复聊天记录错乱
3.修复重启服务器后无法登录
This commit is contained in:
flswld
2023-02-12 23:47:44 +08:00
parent 0b3c075402
commit ddecfdea12
11 changed files with 108 additions and 35 deletions
+3 -18
View File
@@ -249,22 +249,7 @@ func (d *Dao) QueryChatMsgListByUid(uid uint32) ([]*model.ChatMsg, error) {
result := make([]*model.ChatMsg, 0)
find, err := db.Find(
context.TODO(),
bson.D{{"ToUid", uid}},
)
if err != nil {
return nil, err
}
for find.Next(context.TODO()) {
item := new(model.ChatMsg)
err = find.Decode(item)
if err != nil {
return nil, err
}
result = append(result, item)
}
find, err = db.Find(
context.TODO(),
bson.D{{"Uid", uid}},
bson.D{{"$or", []bson.D{{{"ToUid", uid}}, {{"Uid", uid}}}}},
)
if err != nil {
return nil, err
@@ -282,7 +267,7 @@ func (d *Dao) QueryChatMsgListByUid(uid uint32) ([]*model.ChatMsg, error) {
func (d *Dao) ReadAndUpdateChatMsgByUid(uid uint32, targetUid uint32) error {
db := d.db.Collection("chat_msg")
_, err := db.UpdateOne(
_, err := db.UpdateMany(
context.TODO(),
bson.D{{"ToUid", uid}, {"Uid", targetUid}},
bson.D{{"$set", bson.D{{"IsRead", true}}}},
@@ -290,7 +275,7 @@ func (d *Dao) ReadAndUpdateChatMsgByUid(uid uint32, targetUid uint32) error {
if err != nil {
return err
}
_, err = db.UpdateOne(
_, err = db.UpdateMany(
context.TODO(),
bson.D{{"Uid", uid}, {"ToUid", targetUid}},
bson.D{{"$set", bson.D{{"IsRead", true}}}},
+63
View File
@@ -14,12 +14,20 @@ import (
"github.com/vmihailenco/msgpack/v5"
)
// RedisPlayerKeyPrefix key前缀
const RedisPlayerKeyPrefix = "HK4E"
// GetRedisPlayerKey 获取玩家数据key
func (d *Dao) GetRedisPlayerKey(userId uint32) string {
return RedisPlayerKeyPrefix + ":USER:" + strconv.Itoa(int(userId))
}
// GetRedisPlayerLockKey 获取玩家分布式锁key
func (d *Dao) GetRedisPlayerLockKey(userId uint32) string {
return RedisPlayerKeyPrefix + ":USER_LOCK:" + strconv.Itoa(int(userId))
}
// GetRedisPlayer 获取玩家数据
func (d *Dao) GetRedisPlayer(userId uint32) *model.Player {
startTime := time.Now().UnixNano()
playerDataLz4, err := d.redis.Get(context.TODO(), d.GetRedisPlayerKey(userId)).Result()
@@ -54,6 +62,7 @@ func (d *Dao) GetRedisPlayer(userId uint32) *model.Player {
return player
}
// SetRedisPlayer 写入玩家数据
func (d *Dao) SetRedisPlayer(player *model.Player) {
playerData, err := msgpack.Marshal(player)
if err != nil {
@@ -91,9 +100,63 @@ func (d *Dao) SetRedisPlayer(player *model.Player) {
logger.Debug("set player to redis cost time: %v ns", costTime)
}
// SetRedisPlayerList 批量写入玩家数据
func (d *Dao) SetRedisPlayerList(playerList []*model.Player) {
// TODO 换成redis批量命令执行
for _, player := range playerList {
d.SetRedisPlayer(player)
}
}
// 基于redis的玩家离线数据分布式锁实现
const (
MaxLockAliveTime = 10000 // 单个锁的最大存活时间 毫秒
LockRetryWaitTime = 50 // 同步加锁重试间隔时间 毫秒
MaxLockRetryTimes = 2 // 同步加锁最大重试次数
)
// DistLock 加锁并返回是否成功
func (d *Dao) DistLock(userId uint32) bool {
result, err := d.redis.SetNX(context.TODO(),
d.GetRedisPlayerLockKey(userId),
time.Now().UnixMilli(),
time.Millisecond*time.Duration(MaxLockAliveTime)).Result()
if err != nil {
logger.Error("redis lock setnx error: %v", err)
return false
}
return result
}
// DistLockSync 加锁同步阻塞直到成功或超时
func (d *Dao) DistLockSync(userId uint32) bool {
for i := 0; i < MaxLockRetryTimes; i++ {
result, err := d.redis.SetNX(context.TODO(),
d.GetRedisPlayerLockKey(userId),
time.Now().UnixMilli(),
time.Millisecond*time.Duration(MaxLockAliveTime)).Result()
if err != nil {
logger.Error("redis lock setnx error: %v", err)
return false
}
if result == true {
break
}
time.Sleep(time.Millisecond * time.Duration(LockRetryWaitTime))
}
return true
}
// DistUnlock 解锁
func (d *Dao) DistUnlock(userId uint32) {
result, err := d.redis.Del(context.TODO(), d.GetRedisPlayerLockKey(userId)).Result()
if err != nil {
logger.Error("redis lock del error: %v", err)
return
}
if result == 0 {
logger.Error("redis lock del result is fail")
return
}
}