您的位置:首页 > 运维架构 > 网站架构

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

2014-09-07 11:32 405 查看
分类: python2014-08-12
22:36 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
plaincopy





{

"master":{"rootport":9999,"webport":9998},

"servers":{

"gate":{"rootport":10000,"name":"gate","app":"app.gateserver"},

"dbfront":{"name":"dbfront","db":true,"mem":true,"app":"app.dbfrontserver"},

"net":{"netport":1000,"name":"net","remoteport":[{"rootport":10000,"rootname":"gate"}],"app":"app.netserver"},

"game1":{"remoteport":[{"rootport":10000,"rootname":"gate"}],"name":"game1","app":"app.game1server"}

},

"db":{

"host":"localhost",

"user":"root",

"passwd":"",

"port":3306,

"db":"anheisg",

"charset":"utf8"

},

"memcached":{

"urls":["127.0.0.1:11211"],

"hostname":"anheisg"

}

}

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
plaincopy





#coding:utf8

'''''

Created on 2014-8-11

@author: skycrab68@163.com

'''

from gfirefly.server.globalobject import GlobalObject

def doWhenStop():

"""服务器关闭前的处理

"""

print '############'

print 'server stop'

GlobalObject().stophandler = doWhenStop

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

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

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

[python] view
plaincopy





#coding:utf8

'''''

Created on 2014-8-11

@author: skycrab68@163.com

'''

from gfirefly.server.globalobject import GlobalObject, netserviceHandle

"""

net默认service是CommandService(文件server/server.py)

netservice = services.CommandService("netservice")

所以通过'_'分隔命令号

参数选项是通过函数doDataReceived传过来的(文件netconnect/protoc.py)

def doDataReceived(self,conn,commandID,data):

'''数据到达时的处理'''

response = self.service.callTarget(commandID,conn,data)

return response

"""

@netserviceHandle

def nethandle_100(_conn, data):

"""

conn是LiberateProtocol的实例(netconnect/protoc.py)

"""

print "handle_100:",data

return "nethandle_100 ok"

@netserviceHandle

def nethandle_200(_conn, data):

"""200消息请求转发给gateserver处理

remote['gate']是RemoteObject的实例(distributed/node.py)

"""

return GlobalObject().remote['gate'].callRemote("gatehandle",data)

@netserviceHandle

def nethandle_300(_conn, data):

"""300消息请求转发给gateserver处理,gate再调用game1

remote['gate']是RemoteObject的实例(distributed/node.py)

"""

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
plaincopy





#coding:utf8

'''''

Created on 2014-8-11

@author: skycrab68@163.com

'''

from gfirefly.server.globalobject import GlobalObject, rootserviceHandle

@rootserviceHandle

def gatehandle(data):

print "gatehandle:",data

return "gate ok"

@rootserviceHandle

def game1handle(data):

print "gate forward to game1"

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
plaincopy





#coding:utf8

'''''

Created on 2014-8-11

@author: skycrab68@163.com

'''

from gfirefly.server.globalobject import GlobalObject, remoteserviceHandle

"""

"""

@remoteserviceHandle("gate")

def game1end(data):

print "game1end handle",data

return "game1end ok"

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

客户端参数:clienttest.py:

[python] view
plaincopy





#coding:utf8

import time

from socket import AF_INET,SOCK_STREAM,socket

import struct

HOST='localhost'

PORT=1000

BUFSIZE=1024

ADDR=(HOST , PORT)

client = socket(AF_INET,SOCK_STREAM)

client.connect(ADDR)

def sendData(sendstr,commandId):

HEAD_0 = chr(0)

HEAD_1 = chr(0)

HEAD_2 = chr(0)

HEAD_3 = chr(0)

ProtoVersion = chr(0)

ServerVersion = 0

sendstr = sendstr

data = struct.pack('!sssss3I',HEAD_0,HEAD_1,HEAD_2,\

HEAD_3,ProtoVersion,ServerVersion,\

len(sendstr)+4,commandId)

senddata = data+sendstr

return senddata

def resolveRecvdata(data):

head = struct.unpack('!sssss3I',data[:17])

lenght = head[6]

data = data[17:17+lenght]

return data

s1 = time.time()

def start():

for commandId in (100,200,300):

print "----------------"

print "send commandId:",commandId

client.sendall(sendData('asdfe',commandId))

print resolveRecvdata(client.recv(BUFSIZE))

start()

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

启动服务端:



启动客户端:



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

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

预知gfirefly原理,敬请期待[gfirefly深入解析]--gfirefly的基石gtwisted
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: