前面介绍过master模块,现在我们看看dbfront模块,源码在firefly/dbentrust和app/defront 目录。

顾名思义 entrust 就是数据库托管的意思。这个模块实现的功能就是负责从数据库读取数据,并且缓存到memcache。然后定期的检查缓存并写入更新到DB。

刚刚看到9秒论坛里面有篇文章介绍这个dbentrust库的左右。写的很详细。地址如下:

ht空格tp://www.9miao.com/thread-44002-1-1.html

既然文章已经写了很详细的说明,我就偷懒了:)

下面我主要介绍一下db整体模块的结构,流程,逻辑。

前面的章节应该提到过,除了master模块以外,其它模块(db,gate,net,game1,admin)都是通过master的子进程方式启动。启动代码如下:

defstartChildren(self):

"""

"""

print "startchildren ......"

config =json.load(open(self.configpath, 'r'))

sersconf =config.get('servers')

for sername insersconf.keys():

cmds = 'python%s %s %s' % (self.mainpath, sername, self.configpath)

subprocess.Popen(cmds,shell=True)

reactor.run()

通过简单加打印便可以发现,这里其实就是“python appmain.py db config.json”

OK,那么我们可以抛开master,单独命令行启动这个db模块。

为了更加清晰的学习代码,我已经把每个模块单独分离开,具体分离后的代码请看github。 地址为:htt空格ps://github.com/chenee/firefly_study

我们下面自己那这份代码解说,大家可以对照源代码进行学习。

(说明,这份代码只是为了学习才拆分开,会存在很多冗余,甚至不一致的地方。仅供参考)

代码目录如下:

1 .

2 ├── app  #原先的游戏逻辑目录,这个和firefly库目录对应,存放游戏具体实现。但是这里被我打乱了。

3 │   ├── __init__.py

4 │   ├── dbfront #数据库操作相关文件目录

5 │   │   ├── McharacterManager.py #角色管理操作文件,从数据库读取所有角色信息,缓存到memcache

6 │   │   ├── __init__.py

7 │   │   ├── initconfig.py  #db模块中游戏部分的初始化文件,负责app目录的内容的加载。

8 │   │   ├── madminanager.py #MAdmin类的管理类。Madmin下面会提到。

9 │   │   ├── mcharacter.py #角色类,角色在memcache中的映射。

10 │   │   └── memmode.py #几个Madmin类的初始化工作

11 │   ├── dbfrontserver.py  #启动接口,唯一作用就是调用initconfig.py

12 │   ├── logs

13 │   │   └── dbfront.log #log文件

14 │   └── share

15 │       ├──__init__.py

16 │       └──dbopear   #数据库操作文件,对于db模块来说就只使用了一个文件,typo!

17 │          ├── __init__.py

18 │          └── dbCharacter.py #tb_character角色表的select,update封装类。

19 ├── appmain.py  #启动脚本,读config.json配置文件然后初始化DB模块类

20 ├── config.json  #配置文件,非常重要的文件

21 ├── dbpool.py #db连接池,原先的文件只提供初始化和取连接池的2个函数。感觉很多dbopear目录的的sql操作完全可以封装,具体见我game1模块里面的改动,其它几个模块的文件可能不同步。最终会按照game1的模式整合。

22 ├── dbserver.py #db模块的类文件,这个对于原先FFServer。针对每个模块我把他改成对应名称,便于理解

23 ├── globalobject.py #全局类,这里的全局只每个模块内部的全局,而不是整个系统的全局。每个模块自己的globalobject类完全可以不同。

24 ├── leafnode.py #就是原先的node.py,在PB那个章节我们介绍过。

25 ├── logobj.py #log类

26 ├── memclient.py #memcache的客户端实现,提供对memcache的访问操作接口

27 ├── memobject.py #memcached关系对象通过key键的名称前缀来建立

各个key-value 直接的关系; 比如memobject.name= “tbl_role”, 那么memobject.get(“id”)得到的就是tbl_role:id的值。

28 ├── mmode.py #里面包括2个重要的类,MMode,MAdmin;都是memobject的子类,逻辑上MMode代表内存中的一条数据,MAdmin,代表内存中的一张表。而前面madminanager.py就是这些表的管理类。

MAdmin对应memcache的前缀是表名称:如tb_item

MMode对应memcache的前缀是pk(primary key,主键ID)。如 tb_item:1001

那么基本的一条数据组织的格式是:tbl_item:1001 {id:10001, name:chenee , money:10000};也就是memcache的key是 “ 表名称:该条的主键值”,value是这条内容的json格式。

验证方式,可以telnet到memcache打印出来看结果。(以前做的,现在记不清了,可能有误,此刻我自己还木有验证)

29 ├── reference.py #PB相关,看前面一章介绍

30 ├── run.sh #shell启动脚本,为了方便,我自己写的。

31 ├── serviceControl.py #对应原先的一个叫做admin.py的文件,其实就是给leafnode加2条命令(stop,reload)这个在PB章节也说过了。

32 ├── services.py #服务类,前面提过

33 ├── singleton.py #单例类,我blog上面有相关阐述,后面一章我粘贴过来。

34 └── util.py #大部分都是sql查询操作的封装函数。

35

仔细看完上面目录介绍,基本上应该对DB的结构有个大致掌握了。下面我们分析一下源码。

启动db模块的命令:

$cat run.sh

python appmain.py

appmain.py便于学习被我改动过了,如下:

if __name__ == "__main__":

servername ="dbfront"

config =json.load(open("config.json", 'r'))

dbconf =config.get('db')

memconf =config.get('memcached')

sersconf =config.get('servers',{})

masterconf = config.get('master',{})

serconfig =sersconf.get(servername)

ser = DBServer()

ser.config(serconfig,dbconfig=dbconf, memconfig=memconf,masterconf=masterconf)

ser.start()

实际上就是实例化DBServer类,把从config.json文件读取的信息传递过去。DBServer就是原先firefly/server/server.py文件。改个名字好看。

config.json也被我改了一下,“services”里面只保留“dbfront”,其它都services内容都无关。就不贴出来了,占地方。

现在看DBServer(FFServer)类:

class DBServer:

def __init__(self):

"""

"""

self.leafNode =None

self.db = None

self.mem = None

self.servername =None

defconfig(self,config,dbconfig = None,memconfig = None,masterconf=None):

"""配置服务器

"""

servername =config.get('name')#服务器名称

logpath =config.get('log')#日志

hasdb =config.get('db')#数据库连接

hasmem =config.get('mem')#memcached连接

app =config.get('app')#入口模块名称

self.servername =servername

if masterconf:

masterport =masterconf.get('rootport')

addr = ('localhost',masterport)

self.leafNode= leafNode(servername)

self.leafNode.connect(addr)

GlobalObject().leafNode = self.leafNode

if hasdb anddbconfig:

log.msg(str(dbconfig))

dbpool.initPool(**dbconfig)

if hasmem andmemconfig:

urls =memconfig.get('urls')

hostname =str(memconfig.get('hostname'))

mclient.connect(urls, hostname)

if logpath:

log.addObserver(loogoo(logpath))#日志处理

log.startLogging(sys.stdout)

if app:

reactor.callLater(0.1,__import__,app)

def start(self):

"""启动服务器

"""

log.msg('%sstart...'%self.servername)

log.msg('%s pid:%s'%(self.servername,os.getpid()))

reactor.run()

根据config.json的解析结果,我们精简掉所有无关内容。发现,DB模块包括以下几个功能模块:

mastconfig #说明我们需要连接一个root,也就是前面提到的master模块

db #有数据库操作,需要简历数据池

mem #有memcache操作,要连接memcache。

所有连接信息,如ip、port等都是从config.json里面取得。

1、masterconfig部分,就是前面PB章节的介绍,这里实现leafNode去连接master模块的root,就不再赘述了。

2、db pool部分也很简单,就是建立一个pool,提供一个connection的接口。大家去了解DBUtils.PooledDB这个库就可以了。

3、mem部分,也没有啥可说,纯memclient就是调用python的Memcache而已,memcache的结构又超级简单,就是get,set。不含任何逻辑的。想要实现逻辑关系,都要自己去构建,就是上面我们提到的MMode和MAdmin等文件来实现。

OK,firefly库部分的调用完毕,这个时候DB模块已经建立了,和master的PB连接,数据池,memcache连接。下面就是游戏内容部分的实现了。

除了master模块,其它所有模块的游戏部分(app目录下面的内容)都是通过

if app:

reactor.callLater(0.1,__import__,app)

这种方式来import进来的。对我这种python新手还真的迷惑的半天。实际上就是根据config.json里面对于app项的内容。对于db这里展开是:

reactor.callLater(0.1,__import__,app.dbfrontserver)

就是过0.1秒执行 import app.dbfrontserver。其内容如下:

GlobalObject().stophandler = initconfig.doWhenStop

initconfig.loadModule()

loadModule()干3件事情:

def loadModule():

register_madmin()

initData()

CheckMemDB(1800)

注册几个表,初始化角色数据到内存,同步内存数据到数据库

注册表的代码在mmode.py中,过程就是实例化几个MAdmin来表示相应表的结构,然后添加到MAdminManager这个单例管理类中。

MAdmin有几个属性代表表的主键,外键,表名称等信息。

MAdmin的insert函数会调用父类的Memobject的insert函数。

nowdict =dict(self.__dict__)

delnowdict['_client']

newmapping =dict(zip([self.produceKey(keyname) for keyname in nowdict.keys()],

nowdict.values()))

self._client.set_multi(newmapping)

实际上就是根据self的所有属性(除了_client,这个属性指的是memclient)来生成一个字典,然后把这个字典的内容缓存到memcache中。

比如tb_item表对应的MAdmin,生成的memcache内容就包括(不限于)

Key                     value

tb_item:_name      xxxx

tb_item:_lock      xxxx

tb_item:_fk      xxxx

tb_item:_pk    xxxxx

这里其实只是把表结构给缓存到memcache了,压根没有碰表的数据。MAdmin有几个个函数可以取数据,

load()#这个是根据表名称,select * 并且一条一条生成MMode,然后缓存进memcache,MMode前面提到过,代表一条数据的内存对应数据结构。

getObj(self,pk):#先判断pk这条数据是否在memcache,是否有效,如果没有再从数据库取出来并同步到memcache中。

这两条函数其实在db模块启动过程中都没有被调用,(可以加断点或者打印验证)

OK,分析到这里下面在看角色初始化initData()的部分就简单了

def initData(self):

allmcharacter =dbCharacter.getALlCharacterBaseInfo()

for cinfo inallmcharacter:

pid =cinfo['id']

mcha =Mcharacter(pid, 'character%d' % pid, mclient)

mcha.initData(cinfo)

Mcharacter也是MemObject的子类,做的就是根据数据库中的角色信息实例化Mcharacter内存数据,然后调用memobject的insert同步到memcache。

取角色信息的过程相反。调用mcharacterinfo()函数,唯一一点不同是,这个函数有@property修饰,我查了一下,表示这个函数可以当成属性来用,python真酷!

这里吐槽一下注释: 摆明是从啥地方copy过来的,注释的牛头不对马嘴,害的我看了老半天,都木有想明白。

"""初始化城镇要塞对象

@paramterritoryId: int 领地的ID

@param guard:int 殖民者的ID

@paramguardname: str 殖民者的名称

@paramupdateTime: int 领地被更新的时间

"""

最后再唠叨一下checkAdmins();这个函数负责每隔1800(magic number)秒刷一边MAdminManager类管理的所有MAdmin(表)。调用这些MAdmin对应的checkAll();

这个checkAll函数会取得memcache中所有缓存数据,比较是否以本表前缀开头,如果是,则判断这些是否有效,是否过期,是否需要写入数据库。。。。

在我看来,这里有些可以优化的逻辑。比如把取memcache所有数据的步骤提到MAdminManager层面,这样每个MAdmin就不用单独执行一遍。

但是如果是多个memcache服务器,又该怎么办?各种头疼,问题太多,智商不够用。

这个函数是魔鬼,我暂时没有敢去动它,等我多学习学习相关内容再去做优化。

firefly游戏服务器学习笔记 6———— db模块相关推荐

  1. 好程序员web前端分享Nodejs学习笔记之Stream模块

    好程序员web前端分享Nodejs学习笔记之Stream模块 一,开篇分析 流是一个抽象接口,被 Node 中的很多对象所实现.比如对一个 HTTP 服务器的请求是一个流,stdout 也是一个流.流 ...

  2. node.js学习笔记5——核心模块1

    node.js学习笔记5--核心模块1 Node.js核心模块主要内容包括:(1)全局对象 (2)常用工具 (3)事件机制 (4)文件系统访问 (5)HTTP服务器与客户端 一: 全局对象 Node. ...

  3. golang游戏开发学习笔记-创建一个能自由探索的3D世界

    此文写在golang游戏开发学习笔记-用golang画一个随时间变化颜色的正方形之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里,我们将创建一个非常简单(只有三个方块)但能自由探索的的3D世 ...

  4. 【麦可网】Cocos2d-X跨平台游戏开发学习笔记---第二十一课:Cocos2D-X网格特效1-3

    [麦可网]Cocos2d-X跨平台游戏开发---学习笔记 第二十一课:Cocos2D-X网格特效1-3 ================================================ ...

  5. RPG游戏Demo学习笔记一

    导航 RPG游戏Demo学习笔记一 UE笔记 -- 一个简单的AI_weixin_52918492的博客-CSDN博客 目录 一.准备资源 二.基础功能 生命值与能量值 创建Widget Bluepr ...

  6. 【Unity3D游戏开发学习笔记】(六)上帝之手—GameObject的操作

    在Unity中,所有实体都属于游戏对象(GameObject),比如外部导入到场景中的模型,Unity自带的立方体等等,而要将这些GameOject进行管理,交互等操作,则需要用到脚本来实现,上一节我 ...

  7. python学习笔记4(模块

    python学习笔记4(模块) 一.模块 1.1 import语句 1.2 __name__属性 1.3 dir()函数 1.4 包 2.1 导入特定模块: 二.输入与输出 2.1 format使用 ...

  8. 跳跳涂鸦——向上跳动游戏(学习笔记)

    跳跳涂鸦跳跳涂鸦--向上跳动游戏(学习笔记) 一.需求分析 (一).游戏的主要玩法 1.是一款2D向上跳动游戏 主要玩法:player向上跳动,然后有五种类型的砖块,每个砖块有不同的属性.砖块上面有怪 ...

  9. golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)

    此文写在golang游戏开发学习笔记-创建一个能自由探索的3D世界之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里我们要创建一个简单的2D游戏场景以及配套的人物,并实现人物运动和碰撞检测功能 ...

最新文章

  1. Android ProgressBar 加载中界面实现(loading 动画) 实现菊花的效果
  2. iPhone浏览器性能测试
  3. 关系数据库标准语言 SQL (ch.3)
  4. 暂无,进程那篇深度不够
  5. Java 学习笔记 反射与迭代器
  6. V3S中默认时区设置(笔记)
  7. 凡子谷机器人创客教育_【活动纪实】机器人创客教育课堂——液压升降台
  8. markdown html图片,Markdown语法对应的HTML标签实现
  9. StanfordDB class自学笔记 (15) Recursion
  10. Ubuntu 18.04中安装飞行模拟器FlightGear 2018.2.1
  11. S4 HANA BP 维护客户信贷管理数据
  12. shell 脚本中常用的列表
  13. MinGW安装及介绍
  14. 2022年G2电站锅炉司炉报名考试及G2电站锅炉司炉考试技巧
  15. 非常6+1禁播的片段,把李咏笑趴下的经典瞬间:
  16. yolov4与yolov5的区别
  17. MinIO客户端mc使用
  18. 怎么搭建一个高性能服务器
  19. java svfrclient.jar_jp.co.fit.vfreport.SvfrClient.dll,下载,简介,描述,修复,等相关问题一站搞定_DLL之家...
  20. 全色和多光谱融合pansharpen的尝试

热门文章

  1. 看书好还是看视频好?我学五年编程的一点感悟
  2. 企业级负载均衡集群——lvs的DR模式(直接路由模式)详细说明
  3. 送大家一首歌《真心英雄》
  4. 云服务器地域不同有什么区别,云服务器具有地域性区别?如何选择地域节点?...
  5. 联发科技嵌入式_【MTK联发科技工资】嵌入式软件工程师待遇-看准网
  6. Atcoder Codefestival Exhibition/Team Relay/Tournament Round 简要题解
  7. python菜根谭小猫_少儿编程国学动画经典案例_作者:李兴球
  8. 小米手机不用html,小米手机不用三星AMOLED屏幕的真相
  9. 微信小游戏登陆凭证校验出现{errcode:40029,errmsg:invalid code, hints: [ req_id: weh8ka0297hc58 ]}
  10. 花老湿学习OpenCV:分水岭原理和实现watershed()