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

2023-11-09

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:

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

#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:

#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:

#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:

#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


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


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[gfirefly深入解析]--总体架构及demo讲解 的相关文章

  • 如何查看Databricks中的所有数据库和表

    我想列出 Azure Databricks 中每个数据库中的所有表 所以我希望输出看起来像这样 Database Table name Database1 Table 1 Database1 Table 2 Database1 Table
  • 无法“安装”plpython3u - postgresql

    我正在尝试在 postgresql 中使用 python 语言 像这样的事情 create or replace function test a integer returns integer as if a 2 0 return even
  • 使用 psycopg2 在 python 中执行查询时出现“编程错误:语法错误位于或附近”

    我正在运行 Python v 2 7 和 psycopg2 v 2 5 我有一个 postgresql 数据库函数 它将 SQL 查询作为文本字段返回 我使用以下代码来调用该函数并从文本字段中提取查询 cur2 execute SELECT
  • Django 代理模型的继承和多态性

    我正在开发一个我没有启动的 Django 项目 我面临着一个问题遗产 我有一个大模型 在示例中简化 称为MyModel这应该代表不同种类的物品 的所有实例对象MyModel应该具有相同的字段 但方法的行为根据项目类型的不同而有很大差异 到目
  • Python 中的 Lanczos 插值与 2D 图像

    我尝试重新缩放 2D 图像 灰度 图像大小为 256x256 所需输出为 224x224 像素值范围从 0 到 1300 我尝试了两种使用 Lanczos 插值来重新调整它们的方法 首先使用PIL图像 import numpy as np
  • 使用带有关键字参数的 map() 函数

    这是我尝试使用的循环map功能于 volume ids 1 2 3 4 5 ip 172 12 13 122 for volume id in volume ids my function volume id ip ip 我有办法做到这一点
  • 使用 matplotlib 绘制时间序列数据并仅在年初显示年份

    rcParams date autoformatter month b n Y 我正在使用 matpltolib 来绘制时间序列 如果我按上述方式设置 rcParams 则生成的图会在每个刻度处标记月份名称和年份 我怎样才能将其设置为仅在每
  • 如何使用Conda下载python包并随后离线安装?

    我知道通过 pip 我可以使用以下命令下载 Python 包 但 pip install 破坏了我的内部包依赖关系 当我做 pip download
  • 从字符串中删除识别的日期

    作为输入 我有几个包含不同格式日期的字符串 例如 彼得在16 45 我的生日是1990年7月8日 On 7 月 11 日星期六我会回家 I use dateutil parser parse识别字符串中的日期 在下一步中 我想从字符串中删除
  • 如何在 Python 中检索 for 循环中的剩余项目?

    我有一个简单的 for 循环迭代项目列表 在某些时候 我知道它会破裂 我该如何退回剩余的物品 for i in a b c d e f g try some func i except return remaining items if s
  • python 相当于 R 中的 get() (= 使用字符串检索符号的值)

    在 R 中 get s 函数检索名称存储在字符变量 向量 中的符号的值s e g X lt 10 r lt XVI s lt substr r 1 1 X get s 10 取罗马数字的第一个符号r并将其转换为其等效整数 尽管花了一些时间翻
  • 如何从网页中嵌入的 Tableau 图表中抓取工具提示值

    我试图弄清楚是否有一种方法以及如何使用 python 从网页中的 Tableau 嵌入图形中抓取工具提示值 以下是当用户将鼠标悬停在条形上时带有工具提示的图表示例 我从要从中抓取的原始网页中获取了此网址 https covid19 colo
  • Python pickle:腌制对象不等于源对象

    我认为这是预期的行为 但想检查一下 也许找出原因 因为我所做的研究结果是空白 我有一个函数可以提取数据 创建自定义类的新实例 然后将其附加到列表中 该类仅包含变量 然后 我使用协议 2 作为二进制文件将该列表腌制到文件中 稍后我重新运行脚本
  • 如何在seaborn displot中使用hist_kws

    我想在同一图中用不同的颜色绘制直方图和 kde 线 我想为直方图设置绿色 为 kde 线设置蓝色 我设法弄清楚使用 line kws 来更改 kde 线条颜色 但 hist kws 不适用于显示 我尝试过使用 histplot 但我无法为
  • 为字典中的一个键附加多个值[重复]

    这个问题在这里已经有答案了 我是 python 新手 我有每年的年份和值列表 我想要做的是检查字典中是否已存在该年份 如果存在 则将该值附加到特定键的值列表中 例如 我有一个年份列表 并且每年都有一个值 2010 2 2009 4 1989
  • 解释 Python 中的数字范围

    在 Pylons Web 应用程序中 我需要获取一个字符串 例如 关于如何做到这一点有什么建议吗 我是 Python 新手 我还没有找到任何可以帮助解决此类问题的东西 该列表将是 1 2 3 45 46 48 49 50 51 77 使用
  • 使用 Python 绘制 2D 核密度估计

    I would like to plot a 2D kernel density estimation I find the seaborn package very useful here However after searching
  • 如何计算 pandas 数据帧上的连续有序值

    我试图从给定的数据帧中获取连续 0 值的最大计数 其中包含来自 pandas 数据帧的 id date value 列 如下所示 id date value 354 2019 03 01 0 354 2019 03 02 0 354 201
  • 发送用户注册密码,django-allauth

    我在 django 应用程序上使用 django alluth 进行身份验证 注册 我需要创建一个自定义注册表单 其中只有一个字段 电子邮件 密码将在服务器上生成 这是我创建的表格 from django import forms from
  • Rocket UniData/UniVerse:ODBC 无法分配足够的内存

    每当我尝试使用pyodbc连接到 Rocket UniData UniVerse 数据时我不断遇到错误 pyodbc Error 00000 00000 Rocket U2 U2ODBC 0302810 Unable to allocate

随机推荐

  • 5. springBoot-web开发——模板引擎

    1 thymeleaf Thymelaef 是通过他特定语法对 html 的标记做渲染的一个视图层技术 1 1 引入thymeleaf 1 引入thymeleaf的依赖
  • .net 中context、DbContext是什么?

    可以将上下文看作相关 事物 的包装 例如 HttpContext DbContext ObjectContext HttpContext包含任何HTTP相关操作的信息 DbContext包含用于数据库通信的方法和属性 ObjectConte
  • 对数字添加前导0达到固定长度

    近期在处理一个时间展示 要求月日时分秒固定展示2位数字 毫秒固定展示3位数字 传统的作为是根据小于10 小于100等在前面添加特定个数的0 昨晚突然想到 字符串的slice方法可以指定一个负数值来取字符串的后面n个字符 这样在原始数字前面添
  • Win10下AutoCAD2014 +VS2010 + Object2014 安装与配置环境

    Win10下AutoCAD2014 VS2010 Object2014 安装与配置环境 安装前的准备 1 AutoCAD2014安装包 https pan baidu com s 1nK zeBv7tIcipOKcVroo3g 提取码 p7
  • IDEA自动设置作者信息

    一 背景和痛点 以前用Eclipse 忘记设置类的作者信息 是可以使用 之后回车自动出现作者信息的 现在用IDEA不能通过这种方式补全 非常不方便 注意 网上很多教程 说的是新建java类的时候出现作者信息 跟提到的问题痛点是不同的 作者信
  • 实验1:顺序表的基本操作(包括基本代码)

    实验1 顺序表的基本操作 实验目的 1 掌握顺序表的存储结构 2 实现顺序表的基本操作 实验内容 建立一个顺序表L1 11 2 35 41 65 2 1 查找元素5是否存在 2 删除位序为3的元素 3 输出所有元素 4 输出顺序表的长度 5
  • [专利与论文-10]:跟小朋友聊专利

  • C语言经典100例(5)——输入三个整数x,y,z,请把这三个数由小到大输出。

    题目分析 很简单 依次比较三个数的大小进行排序 最后输出即可 代码实现 include
  • VL6180X传感器驱动

    vl6180x i2c c I2C通信 获取VL6180x地址 可以知道7位地址为0x29 写 0x52 读 0x53 写入数据 通过I2C总线向某一寄存器写入一个字节数据 成功 0 失败 1 uint8 VL6180X WriteByte
  • 配置静态路由小实验

    实验拓扑 实验要求 1 使用 eNSP 搭建如下拓扑图 2 基本配置 接口 IP 地址 设备主机名等 3 配置静态路由 使 PC1 PC2 和 PC3 可以互通 4 配置浮动静态路由 使 PC3 通过 AR3 的 G0 0 1 接口访问其他
  • surfaceDestroyed什么时候被调用

    今天看别人的代码 突然有个疑问 surfaceDestroyed这个函数什么时候被调用呢 上网搜了一番 基本都说是surface被销毁的时候 才会调用surfaceDestroyed 问题又来了surface什么时候被销毁呢 大家都知道su
  • python基础案例练习一

    员工管理系统练习 1 显示系统菜单 2 获得用户输入的菜单 3 根据用户的输入判断执行操作 存储员工信息 employee def show menu print 20 员工管理系统菜单 20 print 1 添加员工信息 print 2
  • 考试座位号 C语言

    每个 PAT 考生在参加考试时都会被分配两个座位号 一个是试机座位 一个是考试座位 正常情况下 考生在入场时先得到试机座位号码 入座进入试机状态后 系统会显示该考生的考试座位号码 考试时考生需要换到考试座位就座 但有些考生迟到了 试机已经结
  • 恶意数据包(pcap)下载网站合集

    收集一些恶意数据包下载网站 方便研究学习 https wiki wireshark org SampleCaptures https www netresec com page PcapFiles https www malware tra
  • xml中使用include引入布局

    为了复用布局 使用include方式引用 activity top bar xml 代码如下 需要注意的是 父容器LinearLayout中layout height为wrap content 而不是match parent 以免引入到其他
  • 如何动态创建二维数组[cpp]

    我们常见的用new来 动态创建一维数组 int m std cin gt gt m int 数组名 new int m 动态创建二维数组 方法一 int n 地图的长宽 cin gt gt n int map new int n 创建一个指
  • js 实现颜色值格式转换 rgb和十六进制的转换

    本文章是以prototype原型的方式 给string字符串类型添加方法 用于实现颜色值格式的转换 如果你不用原型方法 那么你只要借鉴实现方法就好了 RGB转换为16进制 String prototype colorHex function
  • Lightgbm多余信息显示

    LightGBM Warning No further splits with positive gain best gain inf 设置参数 verbosity 1 或 verbose 1
  • Python安装教程(2023年,3月)

    一 Python下载 1 进入Python官网 官网地址 https www python org 2 点击 Downloads 展开后点击 Windows 跳转到下载python版本页面 选择 Stable Releases 稳定版本 我
  • [gfirefly深入解析]--总体架构及demo讲解

    gfirefly是开源的分布式游戏服务器端框架 是firefly的gevent版本 想了解更多关于firefly可参考http www oschina net question 947559 147468 这是firefly的官网http