现在的位置: 首页 > 综合 > 正文

[gfirefly深入解析]–总体架构及demo讲解

2017年11月02日 ⁄ 综合 ⁄ 共 7678字 ⁄ 字号 评论关闭
分类: python 96人阅读 评论(0) 收藏 举报

gfirefly是开源的分布式游戏服务器端框架,是firefly的gevent版本,想了解更多关于firefly可参考http://www.oschina.net/question/947559_147468,这是firefly的官网http://firefly.9miao.com/。不过我关注的是gfirefly,主要有两个原因。

1.gfirefly性能更好(官方说法)

2.我对twisted不是很熟,但对gevent比较熟悉,想阅读源码可能gfirefly更合适。

不得不说9秒很有才,由于firefly底层使用了twisted,所以他们开发了一个简易版本的gtwisted,封装了twisted中的Protocol,Factory,Transport等概念,所以导致gfirefly代码和firefly保持惊人的一致。

建议大家可以先看看9秒的wiki文档,下载地址http://firefly.9miao.com/down/Firefly_wiki.CHM

完整的gfirefly包含以下几个组件:

下面将介绍上图的节点:

1. master管理节点  这是用来管理所有节点的节点,如可通过http来关闭所有节点(可回调节点注册的关闭方法),其实master节点也可以理解为是分布式root节点,其它节点都是remote节点

2.net前端节点   net节点是client端连接节点,负责数据包的结束,解包,封包,发送。net节点也是gate节点的分布式节点,由于游戏中流量较大,所以一般net节点只负责解包,封包,然后将解包后的数据转发给gate分布式根节点,处理完毕后再有net节点将处理结果发给client

3.gate分布式根节点  net节点将解包的数据发给gate节点后,gate节点可以自己处理数据返回结果,也可以调用remote子节点处理数据。

4.remote子节点  一般remote子节点都是真正干活的节点

5.dbfront节点   这个节点一般是负责管理memcache和数据库交互的节点

通过以上分析,我们可以很清晰的看出gfirefly的确是分布式的游戏服务器框架。

我们看看gfirefly源码的总体结构:

dbentrust/   主要实现memcache和数据库之间的映射

distributed/   实现了分布式的节点和管理,root.py主要是分布式根节点,node节点实现了远程调用对象的

management/   主要提供了命令行创建项目,类似django的createproject等

master/      master节点相关,web管理接口,以及启动其它节点,通过subprocess模块

netconnect/    net节点的封包解包,以及连接管理

server/    gate等其它节点都是通过server/server.py来实现的

utils/  一些有用工具,如单例metaclass,贯彻节点提供的服务(servers.py) 

gfirefly提供了较为完整的分布式控制,可以通过配置文件开启所需的节点。也许你的项目流量不大,并不需要分布式,或者压根没有数据库,那么只开启net节点就好了。当然net节点一般肯定是需要开启的,下面我们来看看gfirefly的配置文件。

新建简单的一个项目,目录结构如下:

项目可以在我的github上下载:https://github.com/Skycrab/gfirefly/tree/0.16/gfirefly/example/ex_all

因为这个项目涉及到所有的节点,所以我叫ext_all(example_allnode)

配置文件config.json:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. {  
  2. "master":{"rootport":9999,"webport":9998},  
  3. "servers":{  
  4. "gate":{"rootport":10000,"name":"gate","app":"app.gateserver"},  
  5. "dbfront":{"name":"dbfront","db":true,"mem":true,"app":"app.dbfrontserver"},  
  6. "net":{"netport":1000,"name":"net","remoteport":[{"rootport":10000,"rootname":"gate"}],"app":"app.netserver"},  
  7. "game1":{"remoteport":[{"rootport":10000,"rootname":"gate"}],"name":"game1","app":"app.game1server"}  
  8. },  
  9. "db":{  
  10. "host":"localhost",  
  11. "user":"root",  
  12. "passwd":"",  
  13. "port":3306,  
  14. "db":"anheisg",  
  15. "charset":"utf8"  
  16. },  
  17. "memcached":{  
  18. "urls":["127.0.0.1:11211"],  
  19. "hostname":"anheisg"  
  20. }  
  21. }  

1.master定义了两个端口,故名思议,webport就是我们可以通过http端口管理节点,如http://127.0.0.1/stop就是关闭服务器和所有节点。我们上面说过其实master也是其它所有节点的根节点,所以rootport就是监听的节点,其它所有节点会在初始化时连接rootport

2.重点在servers,所谓的servers也就是我们要起的节点。

#gate,因为gate也是其它节点(net节点等)的根节点,所以它需要rootport,也就是说gate将会监听10000端口等待子节点的连接。name就是给gate起个名字,关注一下app,app唯一的作用就是gate节点最后会import app.gateserver,其实也就是运行app.gateserver.py,从上面的项目结构我们看到的确有这个文件。在这个文件里,我们会定义gate将如何处理数据,后面会看到。

#net,我们知道net是client端连接的节点,所以netport也就是net监听的端口,client将向netport这个端口发送数据。重点在remoteport,所谓的remoteport其实就是定义它的父节点,父节点可以有多个,所以是数组。我们看到父节点是10000端口,是gate监听的端口,所以说gate是net的父节点。

#game1,我们看到game1的remoteport中也是有gate节点的,所以gate节点也是game1节点的父节点。因为game1节点并不需要监听其它端口,所以它没有定义自己的rootport。

#defront,前面说过这个节点主要是将表映射到memcache中,所以需要数据库(db:true),需要memcache(mem:true)。其实定义定义db,mem都是说明这个节点需要到数据库和memcache,gfirefly会根据配置文件自动配置全局的memcache client对象,db也是一样。

3.db和mem大家都懂的。

通过以上分析,其实所有的节点关系都是通过配置文件联系的,包括我们所说的gate节点,其实你完全可以定义为其它节点,只不能起gate作用的我们称之为gate而已。

下面我们看一下配置文件中所有的app入口文件。

前端节点:dbfrontserver.py:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. #coding:utf8  
  2. ''''' 
  3. Created on 2014-8-11 
  4.  
  5. @author: skycrab68@163.com 
  6. '''  
  7. from gfirefly.server.globalobject import GlobalObject  
  8.   
  9. def doWhenStop():  
  10.     """服务器关闭前的处理 
  11.     """  
  12.     print '############'  
  13.     print 'server stop'  
  14.   
  15. GlobalObject().stophandler = doWhenStop  

在所有的节点中我们都都可以给stophandler定义一个服务器关闭的处理方法。GlobalObject是个单例模式,翻看源码一看就懂。在这里其实什么事都没有做,由于涉及到

gfirefly的dbentrest的使用,所以后期再具体看看。

net前端监听节点:netserver.py:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. #coding:utf8  
  2. ''''' 
  3. Created on 2014-8-11 
  4.  
  5. @author: skycrab68@163.com 
  6. '''  
  7.   
  8. from gfirefly.server.globalobject import GlobalObject, netserviceHandle  
  9.   
  10.   
  11. """ 
  12. net默认service是CommandService(文件server/server.py) 
  13.     netservice = services.CommandService("netservice") 
  14. 所以通过'_'分隔命令号 
  15.  
  16. 参数选项是通过函数doDataReceived传过来的(文件netconnect/protoc.py) 
  17.     def doDataReceived(self,conn,commandID,data): 
  18.         '''数据到达时的处理''' 
  19.         response = self.service.callTarget(commandID,conn,data) 
  20.         return response 
  21. """  
  22.  
  23. @netserviceHandle  
  24. def nethandle_100(_conn, data):  
  25.     """ 
  26.     conn是LiberateProtocol的实例(netconnect/protoc.py) 
  27.     """  
  28.     print "handle_100:",data  
  29.     return "nethandle_100 ok"  
  30.  
  31. @netserviceHandle  
  32. def nethandle_200(_conn, data):  
  33.     """200消息请求转发给gateserver处理 
  34.     remote['gate']是RemoteObject的实例(distributed/node.py) 
  35.     """  
  36.     return GlobalObject().remote['gate'].callRemote("gatehandle",data)  
  37.  
  38. @netserviceHandle  
  39. def nethandle_300(_conn, data):  
  40.     """300消息请求转发给gateserver处理,gate再调用game1 
  41.     remote['gate']是RemoteObject的实例(distributed/node.py) 
  42.     """  
  43.     return GlobalObject().remote['gate'].callRemote("game1handle",data)  

我们通过netserviceHandle装饰器定义net如何处理数据。gfirefly默认通过commandId发送请求,比如nethandle_100就是处理客户端commandID为100的请求。

这里net我们处理100,200,300请求。

100是net直接处理,也对应了我们前面所说的不需要其它server节点,只需要net节点。

200转发给gate处理,GlobalObject().remote是一个字典,其中有net的所有父节点的远程调用对象,我们看到的remote["gate"]其实就是config.json中net节点父节点的rootname。callRemote("gatehandle",data)也就是调用gate节点的gatehandle方法,传递参数data。看下面gateserver.py,你会发现有这个函数。

300也是转发给gate处理的,只不过gate会交给game1处理,看下面gateserver.py,你会发现game1handle这个函数。

gate分布式分发节点:gateserver.py:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. #coding:utf8  
  2. ''''' 
  3. Created on 2014-8-11 
  4.  
  5. @author: skycrab68@163.com 
  6. '''  
  7. from gfirefly.server.globalobject import GlobalObject, rootserviceHandle  
  8.  
  9. @rootserviceHandle  
  10. def gatehandle(data):  
  11.     print "gatehandle:",data  
  12.     return "gate ok"  
  13.  
  14. @rootserviceHandle  
  15. def game1handle(data):  
  16.     print "gate forward to game1"  
  17.     return GlobalObject().root.callChild("game1","game1end",data)  

我们通过rootserviceHandle定义gate节点处理的函数,因为gate是根节点,所以用rootserviceHandle很贴切。

gatehandle函数就是处理net发过来的200请求,gate直接自己处理,并将“gate ok"返回给net,net再发给client。

game1handle函数就是处理net的300请求,我们给子节点game1处理,GlobalObject().root保存了分布式根节点的实例,通过callChild调用孩子节点的方法。”game1"是孩子节点的名字,"game1end"就是调用孩子节点的game1end方法,data是传递的参数。

game1孩子节点:game1server.py:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. #coding:utf8  
  2. ''''' 
  3. Created on 2014-8-11 
  4.  
  5. @author: skycrab68@163.com 
  6. '''  
  7. from gfirefly.server.globalobject import GlobalObject, remoteserviceHandle  
  8.   
  9. """ 
  10. """  
  11. @remoteserviceHandle("gate")  
  12. def game1end(data):  
  13.     print "game1end handle",data  
  14.     return "game1end ok"  

因为game1是远程节点,所有通过remoteserviceHandle装饰器注册,参数“gate"就是父节点的名字,因为可能不止一个父节点,所以要通过名字来唯一确定。game1end处理的是net的300请求,到game1已经是叶子节点了,所以需要返回数据。

客户端参数:clienttest.py:

[python] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. #coding:utf8  
  2.   
  3. import time  
  4.   
  5. from socket import AF_INET,SOCK_STREAM,socket  
  6. import struct  
  7. HOST='localhost'  
  8. PORT=1000  
  9. BUFSIZE=1024  
  10. ADDR=(HOST , PORT)  
  11. client = socket(AF_INET,SOCK_STREAM)  
  12. client.connect(ADDR)  
  13.   
  14. def sendData(sendstr,commandId):  
  15.     HEAD_0 = chr(0)  
  16.     HEAD_1 = chr(0)  
  17.     HEAD_2 = chr(0)  
  18.     HEAD_3 = chr(0)  
  19.     ProtoVersion = chr(0)  
  20.     ServerVersion = 0  
  21.     sendstr = sendstr  
  22.     data = struct.pack('!sssss3I',HEAD_0,HEAD_1,HEAD_2,\  
  23.                        HEAD_3,ProtoVersion,ServerVersion,\  
  24.                        len(sendstr)+4,commandId)  
  25.     senddata = data+sendstr  
  26.     return senddata  
  27.   
  28. def resolveRecvdata(data):  
  29.     head = struct.unpack('!sssss3I',data[:17])  
  30.     lenght = head[6]  
  31.     data = data[17:17+lenght]  
  32.     return data  
  33.   
  34. s1 = time.time()  
  35.   
  36. def start():  
  37.     for commandId in (100,200,300):  
  38.         print "----------------"  
  39.         print "send commandId:",commandId  
  40.         client.sendall(sendData('asdfe',commandId))  
  41.         print resolveRecvdata(client.recv(BUFSIZE))  
  42.   
  43. start()  

通过客户端,我们向net发送commandID分别为100,200,300的请求,然后打印返回结果。

启动服务端:

启动客户端:

结果是完美的,我们看到gfirefly就是这么简单实现了分布式架构,通过装饰器定义各个节点如何处理数据,完全透明,我们可以完全不懂内部原理,只需要理解几个装饰器的作用,就可以完成复杂的分布式控制。

我想读到这里的你肯定既为gfirefly的强大和简单感到折服,心里肯定也很好奇这些到底是怎么实现的。

预知gfirefly原理,敬请期待[gfirefly深入解析]--gfirefly的基石gtwisted

抱歉!评论已关闭.