游戏服务器ID生成器组件

简介: 游戏服务器程序中,经常需要生成全局的唯一ID号,这个功能很常用,本文将介绍一种通用ID生成组件。游戏服务器程序中使用此组件的场景有:  创建角色时,为其分配唯一ID   创建物品时,每个物品需要唯一ID   创建宝宝、灵兽时需要唯一ID 原理介绍          ID生成器的原理就是使用全局整型变量,每次分配之后该变量递增1。

    游戏服务器程序中,经常需要生成全局的唯一ID号,这个功能很常用,本文将介绍一种通用ID生成组件。游戏服务器程序中使用此组件的场景有:

  •  创建角色时,为其分配唯一ID
  •   创建物品时,每个物品需要唯一ID
  •   创建宝宝、灵兽时需要唯一ID

原理介绍

         ID生成器的原理就是使用全局整型变量,每次分配之后该变量递增1。由于服务器重启后全局变量失效,故全局变量需要持久化保存,相应的,服务器启动时从持久化中载入全局变量。ID生成器的工作流程为:

  •   建议采用数据库作为持久化存储,本文以mysql为例
  •   启动时从数据库载入全局变量,作为分配的起始值
  •   每次分配id前,先递增全局变量
  •   每次递增后,更新数据库中的全局变量值
  •   由于相同的功能模块可能在不同的GameServer上运行,故唯一ID号使用64位整型,其中16位用来表示GameServerID
  •   由于不同的功能即使ID号相同互不影响,如角色ID和物品ID实际上是独立的互不冲突的,所以全局变量可以分类型,不同的类型使用不同全局变量。

示例代码

 

sql = """
create table id_generator
(
  AUTO_INC_ID bigint not null,
  TYPE int not null,
  SERVER_ID int not null,
  RUNING_FLAG int not null,
  primary key(TYPE, SERVER_ID)
)
"""
class idgen_t:
    def __init__(self, db_host_, type_id_ = 0, server_id_ = 0):
        self.type_id = type_id_
        self.server_id = server_id_
        self.auto_inc_id = 0
        self.db_host = db_host_
        self.db      = None
        self.saving_flag = False
        self.runing_flag = 0
    def init(self):
        self.db = ffext.ffdb_create(self.db_host)
        ret = self.db.sync_query("SELECT `AUTO_INC_ID`, `RUNING_FLAG` FROM `id_generator` WHERE `TYPE` = '%d' AND `SERVER_ID` = '%d'" % (self.type_id, self.server_id))
        #print(ret.flag, ret.result, ret.column)
        if len(ret.result) == 0:
            #数据库中还没有这一行,插入
            self.db.sync_query("INSERT INTO `id_generator` SET `AUTO_INC_ID` = '0',`TYPE` = '%d', `SERVER_ID` = '%d', `RUNING_FLAG` = '1' " % (self.type_id, self.server_id))
            return True
        else:
            self.auto_inc_id = int(ret.result[0][0])
            self.runing_flag = int(ret.result[0][1])
            if self.runing_flag != 0:
                self.auto_inc_id += 10000
                ffext.ERROR('last idgen shut down not ok, inc 10000')
            self.db.sync_query("UPDATE `id_generator` SET `RUNING_FLAG` = '1' WHERE `TYPE` = '%d' AND `SERVER_ID` = '%d'" % (self.type_id, self.server_id))
        #if self.auto_inc_id < 65535:
        #    self.auto_inc_id = 65535
        return True
    def cleanup(self):
        db = ffext.ffdb_create(self.db_host)
        now_val = self.auto_inc_id
        db.sync_query("UPDATE `id_generator` SET `AUTO_INC_ID` = '%d', `RUNING_FLAG` = '0' WHERE `TYPE` = '%d' AND `SERVER_ID` = '%d'" % (now_val, self.type_id, self.server_id))
        return True
    def gen_id(self):
        self.auto_inc_id += 1
        self.update_id()
        low16 = self.auto_inc_id & 0xFFFF
        high  = (self.auto_inc_id >> 16) << 32
        return high | (self.server_id << 16)| low16
    def dump_id(self, id_):
        low16 = id_ & 0xFFFF
        high  = id_ >> 32
        return high << 16 | low16
    def update_id(self):
        if True == self.saving_flag:
            return
        self.saving_flag = True
        now_val = self.auto_inc_id
        def cb(ret):
            #print(ret.flag, ret.result, ret.column)
            self.saving_flag = False
            if now_val < self.auto_inc_id:
                self.update_id()
        self.db.query("UPDATE `id_generator` SET `AUTO_INC_ID` = '%d' WHERE `TYPE` = '%d' AND `SERVER_ID` = '%d' AND `AUTO_INC_ID` < '%d'" % (now_val, self.type_id, self.server_id, now_val), cb)
        return

 

 

总结

  •   如果不同区组的GameServer使用不同的ID,那么即使合区,那么老的ID仍然可以工作
  •   全局变量递增后,立即更新db,保证尽量保证db和内存的一致性,但是保证同一时间只有一个db 操作执行,db update回调后检查若又被修改后,再次执行db update。比如一次分配了100个id,避免出现“惊群”的db update。
  •   由于递增id后立即执行了db update,几乎可以保证db和内存的一致。如果出现运行期宕机,可能会出现db和内存的不一致。可以在此基础上做一个加强版,就是数据库中的每一行都加一个字段running,每次db update都设置为0,服务器正常关闭的时候设置为1。启动服务器载入全局变量时,若该值为0,则在此基础上增加10000。这样可以保证所有的ID都不会重复。
目录
相关文章
VS2005(excel2007)利用Automation(OLD Automation)方法。将Excel当成组件服务器的编译错误 我的系统盘和office都装在d盘。 自动生成的import有问题
VS2005(excel2007)利用Automation(OLD Automation)方法。将Excel当成组件服务器的编译错误 我的系统盘和office都装在d盘。 自动生成的import有问题
|
9月前
|
Sentinel 微服务
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)2
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)2
66 0
|
6月前
|
监控 前端开发 JavaScript
nodejs循序渐进-高性能游戏服务器框架pomelo之启动流程和组件
nodejs循序渐进-高性能游戏服务器框架pomelo之启动流程和组件
|
8月前
|
缓存 前端开发 数据库
SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器试读版
SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器试读版
48 0
|
8月前
|
小程序 JavaScript
微信小程序 tdesign图片上传组件 上传到服务器
微信小程序 tdesign图片上传组件 上传到服务器
|
9月前
|
消息中间件 监控 Java
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)1
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)1
102 0
|
数据库
SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器试读版
SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器试读版
|
NoSQL MongoDB 数据库
分布式服务器框架之Server.Core库中实现YFUniqueEntity、YFUniqueIDBase 管理MongoDB 自定义Id的自增
YFUniqueEntity是数据库中的结构,GetUniqueID函数中会根据Type和自增步长去数据库中寻找该类型的当前ID是多少,然后会用当前的Id去加上步长,把更新后的新ID插入到MongoDB中记录着ID的那张表里。
|
SQL NoSQL MongoDB
分布式服务器框架之Servers.Core库中实现MongoDB对象实体类 管理对象ID
MongoDB中的ID是一个hash码,和传统的关系数据库相比MongoDB没有办法实现ID的自增,如果需要ID自增的话,所以要自己管理ID,并且把对应类型的ID写入到一个专门的Collection(其实就是Sql中的Table)里面去,每次创建新对象的时候,都要取到该类型当前的ID,然后在这个基础上+1,比如当前是角色的ID是1,又创建了一个新的角色,要在原来的基础上+1 ID=2。今天先把MongoDB的对象实体基类实现了。
|
弹性计算 运维 负载均衡
弹性计算 ECS02-ECS 的组件| 学习笔记
快速学习弹性计算 ECS02-ECS 的组件。
261 0
弹性计算 ECS02-ECS 的组件| 学习笔记