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