game_sync/dbproxy/svc/u_playerbucketid.go

175 lines
4.2 KiB
Go

package svc
import (
"errors"
"math/rand"
"sync"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/game/dbproxy/mongo"
"mongo.games.com/game/model"
)
/*
玩家id生成算法
*/
const (
PlayerBucketIDMin = 10000000 // 第一个玩家id
BatchPlayerBucketIDCnt = 99999999 // 玩家id自增步长
)
var (
PlayerBucketIdsDBName = "user"
PlayerBucketIdsCollName = "user_bucketids"
PlayerBucketIdsAutoId = bson.ObjectIdHex("60f69b4a09dbe3323c632f25") // 记录id使用到多少了
ErrPlayerBucketIdsDBNotOpen = model.NewDBError(PlayerBucketIdsDBName, PlayerBucketIdsCollName, model.NOT_OPEN)
playerBucketId = &model.PlayerBucketId{}
playerBucketIdsCursor = int32(0)
playerBucketIdLock = sync.Mutex{}
)
type PlayerBucketAutoId struct {
Id bson.ObjectId `bson:"_id"`
AutoId int
}
func PlayerBucketIdCollection() *mongo.Collection {
s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, PlayerBucketIdsDBName)
if s != nil {
c, first := s.DB().C(PlayerBucketIdsCollName)
if first {
var id PlayerBucketAutoId
err := c.Find(bson.M{"_id": PlayerBucketIdsAutoId}).One(&id)
if err != nil && errors.Is(err, mgo.ErrNotFound) {
id.Id = PlayerBucketIdsAutoId
id.AutoId = PlayerBucketIDMin
c.Insert(id)
}
}
return c
}
return nil
}
// id 增加
func autoIncPlayerBucketId() (int, error) {
c := PlayerBucketIdCollection()
if c == nil {
return 0, ErrPlayerBucketIdsDBNotOpen
}
change := mgo.Change{
Update: bson.M{"$inc": bson.M{"autoid": BatchPlayerBucketIDCnt}},
ReturnNew: true,
}
doc := PlayerBucketAutoId{}
_, err := c.Find(bson.M{"_id": PlayerBucketIdsAutoId}).Apply(change, &doc)
if err == nil {
return doc.AutoId, nil
}
return 0, err
}
type PlayerBucketId struct {
Id bson.ObjectId `bson:"_id"`
StartPos int32
EndPos int32
Used bool
}
func (receiver *PlayerBucketId) IsValid(cursor int32) bool {
return receiver.Id.Valid() &&
receiver.StartPos != 0 &&
receiver.EndPos != 0 &&
receiver.StartPos != receiver.EndPos &&
cursor >= receiver.StartPos &&
cursor <= receiver.EndPos &&
cursor != 0
}
// 生成一批玩家id
func genABatchPlayerBucketId() error {
maxV, err := autoIncPlayerBucketId()
if err != nil {
return err
}
var (
minV = maxV - BatchPlayerBucketIDCnt
bucketSize = 100 // 最长100个玩家id是连续的
bucketArr []*PlayerBucketId
)
cnt := ((maxV - minV) / bucketSize) + 1
bucketArr = make([]*PlayerBucketId, 0, cnt)
for i := minV; i < maxV; i = i + bucketSize {
bucketArr = append(bucketArr, &PlayerBucketId{
Id: bson.NewObjectId(),
StartPos: int32(i),
EndPos: int32(i + bucketSize - 1),
Used: false,
})
}
for i, _ := range bucketArr {
rnd := rand.Intn(len(bucketArr))
bucketArr[i], bucketArr[rnd] = bucketArr[rnd], bucketArr[i]
}
c := PlayerBucketIdCollection()
docs := make([]interface{}, 0, len(bucketArr))
for _, log := range bucketArr {
docs = append(docs, log)
}
if c != nil {
for len(docs) > 0 {
cnt := len(docs)
if cnt > 1000 {
cnt = 1000
}
err = c.Insert(docs[:cnt]...)
docs = docs[cnt:]
}
}
return err
}
// GetOnePlayerIdFromBucket 获取一个玩家id
func GetOnePlayerIdFromBucket() (pid int32, err error) {
playerBucketIdLock.Lock()
defer playerBucketIdLock.Unlock()
if !playerBucketId.IsValid(playerBucketIdsCursor) {
var flag bool
c := PlayerBucketIdCollection()
if c != nil {
change := mgo.Change{
Update: bson.M{"$set": bson.M{"used": true}},
ReturnNew: true,
}
redo:
_, err := c.Find(bson.M{"used": false}).Apply(change, playerBucketId)
if err != nil {
logger.Logger.Warnf("GetOnePlayerIdFromBucket Find failed:%v", err)
if !flag && errors.Is(err, mgo.ErrNotFound) {
err = genABatchPlayerBucketId()
flag = true
if err == nil {
goto redo
}
}
return 0, err
}
err = c.RemoveId(playerBucketId.Id)
if err != nil {
logger.Logger.Warnf("GetOnePlayerIdFromBucket RemoveId(%v) failed:%v ", playerBucketId.Id, err)
}
playerBucketIdsCursor = playerBucketId.StartPos
} else {
return 0, ErrPlayerBucketIdsDBNotOpen
}
}
pid = playerBucketIdsCursor
playerBucketIdsCursor++
return pid, nil
}