游戏服务器程序中,经常需要生成全局的唯一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都不会重复。
更多精彩文章 http://h2cloud.org
游戏服务器ID生成器组件的更多相关文章
-
来吧,自己动手撸一个分布式ID生成器组件
在经过了众多轮的面试之后,小林终于进入到了一家互联网公司的基础架构组,小林目前在公司有使用到架构组研究到分布式id生成器,前一阵子大概看了下其内部的实现,发现还是存在一些架构设计不合理之处.但是又由于 ...
-
游戏服务器生成全局唯一ID的几种方法
在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...
-
Unity3d&;C#分布式游戏服务器ET框架介绍-组件式设计
前几天写了<开源分享 Unity3d客户端与C#分布式服务端游戏框架>,受到很多人关注,QQ群几天就加了80多个人.开源这个框架的主要目的也是分享自己设计ET的一些想法,所以我准备写一系列 ...
-
Scut游戏服务器免费开源框架-3
Scut游戏服务器免费开源框架--快速开发(3) Scut快速开发(3) 1 开发环境 需要安装的软件 a) 消息队列 b) 数据库,Sql2005以上版本 ...
-
游戏服务器设计之NPC系统
游戏服务器设计之NPC系统 简介 NPC系统是游戏中非常重要的系统,设计的好坏很大程度上影响游戏的体验.NPC在游戏中有如下作用: 引导玩家体验游戏内容,一般游戏内有很多主线.支线任务,而任务的介绍. ...
-
老出BUG怎么办?游戏服务器常见问题解决方法分享
在游戏开发中,我们经常会遇到一些技术难题,而其引发的bug则会影响整个游戏的品质.女性向手游<食物语>就曾遇到过一些开发上的难题,腾讯游戏学院专家团Wade.Zc.Jovi等专家为其提供了 ...
-
游戏服务器菜鸟之C#初探一游戏服务
本人80后程序猿一枚,原来搞过C++/Java/C#,因为工作原因最后选择一直从事C#开发,因为读书时候对游戏一直比较感兴趣,机缘巧合公司做一个手游的项目,我就开始游戏服务器的折腾之旅. 游戏的构架是 ...
-
Redis在游戏服务器中的应用
排行榜游戏服务器中涉及到很多排行信息,比如玩家等级排名.金钱排名.战斗力排名等.一般情况下仅需要取排名的前N名就可以了,这时可以利用数据库的排序功能,或者自己维护一个元素数量有限的top集合.但是有时 ...
-
业务系统需要什么样的ID生成器
业务系统需要什么样的ID生成器 ID 生成器在微博我们一直叫发号器,微博就是用这样的号来存储,而我微博里讨论的时候也都是以发号器为标签.它的主要目的确如平常大家理解的“为一个分布式系统的数据objec ...
随机推荐
-
LYDSY模拟赛day1 Walk
/* 依旧考虑新增 2^20 个点. i 只需要向 i 去掉某一位的 1 的点连边. 这样一来图的边数就被压缩到了 20 · 2^20 + 2n + m,然后 BFS 求出 1 到每个点的最短路即可. ...
-
windows环境下修改Mysql的root密码
最近一直没用Mysql,root密码给忘了,然后就自己修改了一下,现在整理一下步骤.(我的版本是5.1) 启动MySQL服务:>>net start mysql 关闭MySQL服务:> ...
-
WKWebView 与 UIWebView
apple和google为webkit该浏览器引擎的发扬光大做出了重要贡献,在WWDC 2014发布会上发布iOS 8中,apple公布了WebKit框架,这意味着OSX和IOS开发者将共用同样的开发 ...
-
Java对Excel表格的操作
import java.io.File;//引入类import java.io.IOException;import java.util.Scanner;import jxl.Cell;import ...
-
重新认识JavaScript
JavaScrpit在我眼中一直是web前端脚本语言,而这段时间的一些工作,让我对JavaScript有了一个全新的认识. 公司准备启动的一个手游项目,服务器端准备使用网易的开源框架pomelo.po ...
-
C# 快速高效率复制对象的几种方式
http://www.cnblogs.com/emrys5/p/expression_trans_model.html 这篇较具体. 本文基于上文略加改动,暂记 using Newtonsoft.Js ...
-
再探Circuit Breaker之使用Polly
前言 上一篇介绍了使用Steeltoe来处理服务熔断,这篇我们将用Polly来处理服务熔断. 不废话了,直接进正题. 简单的例子 同样先定义一个简单的服务. [Route("api/[con ...
-
CSS3_过渡_2D 变换_瓶体旋转_动态时钟
1. 过渡 transition 允许 CSS 的属性值在一定时间内平滑的过渡, 在鼠标点击,鼠标滑过或对属性改变中触发,并圆滑的改变 CSS 的属性值 简写属性: #box { width: 300 ...
-
python3基础概念
数: str:使用单引号或双引号表达,不可变的,一旦创建不可更改,可以使变量赋予不同的字符串,但字符串本身是没有更改的: list:有序的集合,可变的基础数据类型: clear(),copy(),ap ...
-
BZOJ1251序列终结者——非旋转treap
题目描述 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列 要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技 ...