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

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包含以下几个组件:

[gfirefly深入解析]--总体架构及demo讲解_第1张图片


下面将介绍上图的节点:

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源码的总体结构:

[gfirefly深入解析]--总体架构及demo讲解_第2张图片

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)

[gfirefly深入解析]--总体架构及demo讲解_第3张图片

配置文件config.json:

{
"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:

#coding:utf8
'''
Created on 2014-8-11

@author: [email protected]
'''
from gfirefly.server.globalobject import GlobalObject

def doWhenStop():
    """服务器关闭前的处理
    """
    print '############'
    print 'server stop'

GlobalObject().stophandler = doWhenStop

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

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


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

#coding:utf8
'''
Created on 2014-8-11

@author: [email protected]
'''

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:

#coding:utf8
'''
Created on 2014-8-11

@author: [email protected]
'''
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:

#coding:utf8
'''
Created on 2014-8-11

@author: [email protected]
'''
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:

#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深入解析]--总体架构及demo讲解_第4张图片


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

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

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


本节代码:https://github.com/Skycrab/gfirefly-example


你可能感兴趣的:([gfirefly深入解析]--总体架构及demo讲解)