firefly(暗黑世界版v1.4)教程(一) 程序入口

  • 程序基本結(jié)構(gòu)
1575045854(1).png
62adff4b92e6d0ef67effdbf9b2e4cb.png
由上圖可知趁冈,基本的程序結(jié)構(gòu)為:
master
gate
net
還有若干service模塊(也就是圖中的game1茧球,game2谨读,包括后面提到的admin...)
而連接這些模塊的則是twisted內(nèi)置的pb(透明代理協(xié)議)這部分的內(nèi)容雏搂,如果有興趣椒袍,可以從twisted的手冊上自行查看驼唱,這里就不做詳細(xì)分析,基本就是驹暑,連接一旦建立玫恳,雙方可以互相調(diào)用協(xié)議內(nèi)部商定的函數(shù)。
  • 程序基本流程
381867965365839971.png

暗黑世界版 firfly 基本程序流程如上所示
首先通過master分別模塊啟動
gate(網(wǎng)關(guān))优俘,db(數(shù)據(jù)庫相關(guān))京办,net(網(wǎng)絡(luò)),chat(聊天)帆焕,game(游戲邏輯) 模塊惭婿。
然后各個模塊分別調(diào)用initconfig進(jìn)行初始化并運(yùn)行。

  • 模塊分析

在 暗黑世界 firefly 版中,程序主要分以下幾大模塊

1审孽、master 模塊(在分布式節(jié)點(diǎn)中也叫做master節(jié)點(diǎn)):
  管理模塊 
  主要功能:
         - 調(diào)用各個節(jié)點(diǎn)的 stop (模塊停止)
         - 調(diào)用各個節(jié)點(diǎn)的 reload (模塊重載)
         - 調(diào)用各個節(jié)點(diǎn)的 remoteconnect(模塊連接其他節(jié)點(diǎn))
  該模塊同時啟動一個webserver
  簡單的通過監(jiān)聽本機(jī)9998端口
  管理員可以通過用get方法來獲取用戶管理命令
  目前默認(rèn)的是2條命令县袱,stop和reload
  負(fù)責(zé)其它模塊的stop,reload功能佑力。

  示例:只要在本機(jī)瀏覽器輸入:http://localhost:9998/stop 
  或者
  http://localhost:9998/reload即可式散。

2、dbfront:數(shù)據(jù)庫前端模塊 
  該模塊為獨(dú)立模塊打颤,本身和各個節(jié)點(diǎn)之間不進(jìn)行連接
  負(fù)責(zé)管理DB和Memcache暴拄。
  比如load用戶信息到memcache中
  定期(系統(tǒng)寫死了1800秒)刷新并同步memcache.

4、net:網(wǎng)絡(luò)模塊(直面客戶端的節(jié)點(diǎn)编饺,負(fù)責(zé)與客戶端通信)
  負(fù)責(zé)監(jiān)聽客戶端的網(wǎng)絡(luò)連接乖篷,將客戶端發(fā)送過來的數(shù)據(jù)驗證并解析成指令,然后將指令轉(zhuǎn)送至gate節(jié)點(diǎn)交于gate節(jié)點(diǎn)處理透且,并根據(jù)返回的數(shù)據(jù)回復(fù)客戶端撕蔼。

5、gate:網(wǎng)關(guān)節(jié)點(diǎn)(調(diào)度秽誊,分配各個節(jié)點(diǎn)工作的root節(jié)點(diǎn))
  其它模塊(除了dbfront)都會和這個模塊掛接鲸沮。
  - 將net節(jié)點(diǎn)解析好的數(shù)據(jù),通過游戲邏輯去調(diào)用 service 節(jié)點(diǎn)工作锅论,并將返回的數(shù)據(jù)返回給net節(jié)點(diǎn)讼溺。

5、game1(service節(jié)點(diǎn)最易,該節(jié)點(diǎn)一般為一個以上怒坯,布局在不同內(nèi)核,甚至不同主機(jī)中來均衡負(fù)載):
  暗黑世界的游戲模塊
  這個模塊主要提供所有的游戲邏輯計算服務(wù)
    比如角色升級的經(jīng)驗等級藻懒,各種npc信息剔猿,各種掉落信息,各種戰(zhàn)斗陣型...

6嬉荆、admin:系統(tǒng)管理員模塊
  其實這個模塊對于游戲本身來說艳馒,可有可無。
  主要作用就是導(dǎo)出游戲統(tǒng)計數(shù) 據(jù)员寇,比如在線人數(shù)弄慰,每天充值數(shù)量等等。
  總的來說蝶锋,這個模塊可有可無陆爽,并不重要。
  • 配置信息

該應(yīng)用的所有配置都存放于config.json文件中扳缕,是整個游戲系統(tǒng)以及firefly模塊的核心之一慌闭,這個文件的配置直接決定了服務(wù)器以什么樣的方式運(yùn)行

//config.json
{
  #master節(jié)點(diǎn)的運(yùn)行信息别威,包含host,port
  "master":{
    "roothost":"localhost",
    "rootport":9999,
    "webport":9998
  },
  #servers節(jié)點(diǎn)下運(yùn)行的就是真正工作的節(jié)點(diǎn)
  "servers":{
    #gate節(jié)點(diǎn)的配置信息
    "gate":{
      "rootport":10000, # gate節(jié)點(diǎn)的port
      "name":"gate",     # gate節(jié)點(diǎn)名字(分布式布局中驴剔,名字是辨別節(jié)點(diǎn)的重要標(biāo)志省古,不能重復(fù))
      "db":true, # 是否使用數(shù)據(jù)庫
      "mem":true, # 是否是用memcache
      "app":"app.gateserver", # import 的 文件路徑
      "log":"app/logs/gate.log" # 文件日志路徑
  },
  # 數(shù)據(jù)庫服務(wù)
  "dbfront": {
    "name":"dbfront",
    "db":true,
    "mem":true,
    "app":"app.dbfrontserver",
    "log":"app/logs/dbfront.log"},
    # 網(wǎng)絡(luò)服務(wù)
    "net":{
      "netport":11009, #網(wǎng)絡(luò)運(yùn)行的端口
      "name":"net", # 網(wǎng)絡(luò)節(jié)點(diǎn)的名稱
      "remoteport":[{  # 網(wǎng)絡(luò)節(jié)點(diǎn)需要連接的遠(yuǎn)程節(jié)點(diǎn)信息
                               # 在這里沒有配置host信息,但是實際上pb協(xié)議是支持遠(yuǎn)程部署的丧失,但是firefly源碼上并不支持豺妓,如果需要支持遠(yuǎn)程部署,則需要自動動手修改firefly源碼
         "rootport":10000, # 遠(yuǎn)程節(jié)點(diǎn)端口
         "rootname":"gate" # 遠(yuǎn)程節(jié)點(diǎn)名稱
      }],
      "app":"app.netserver", # net 節(jié)點(diǎn)模塊 import 路徑
      "log":"app/logs/net.log" # net  節(jié)點(diǎn)日志路徑
   },
   # game模塊
   "game1":{ 
     # 同理布讹,這里保存的是需要連接的遠(yuǎn)程gate對象信息
     "remoteport":[{
       "rootport":10000,
       "rootname":"gate"
    }],
    "name":"game1", # 節(jié)點(diǎn)名稱
    "db":true, 
    "mem":true,  
    "app":"app.gameserver",
    "reload":"app.game.doreload", # 模塊重載的方法路徑
    "log":"app/logs/game1.log"},
    # admin節(jié)點(diǎn)信息
    "admin":{
      "remoteport":[{
        "rootport":10000,
        "rootname":"gate"
      }],
    "webport":2012,
    "name":"admin",
    "db":true,
    "mem":true,
    "app":"app.adminserver",
    "log":"app/logs/admin.log"
    }
  },
  # 數(shù)據(jù)庫配置信息
  "db":{
    "host":"localhost",
    "user":"root",
    "passwd":"111",
    "port":3306,
    "db":"anheisg",
    "charset":"utf8"
  },
 # memcached 配置信息
  "memcached":{
    "urls":["127.0.0.1:11211"],
    "hostname":"manman"
  }
}
暗黑世界游戲源碼基本文件結(jié)構(gòu)
-app: 服務(wù)進(jìn)程
    -gate
    -dbfront
    -net
    -game
    -admin(系統(tǒng)管理員模塊,對于框架本身沒有太大意義)

看完配置文件琳拭,我們了解了基本文件結(jié)構(gòu)后,我們再來看看軟件是怎么運(yùn)行的描验。

  • 程序入口
#startmaster.py 文件
if __name__ == "__main__":
    from master import Master
    master = Master('config.json')
    master.start()

從上面可以看出白嘁,軟件通過startmaster文件創(chuàng)建一個master對象
master對象內(nèi)通過start來啟動程序

  • master

既然是通過master開啟的服務(wù),那么我們就深入到master文件一探究竟

-master文件目錄如下:
    - __init__.py
    - master.py 
    - rootapp.py
    - webapp.py
# 在firefly中膘流,每個節(jié)點(diǎn)下面都會有xxxapp命名的文件絮缅,該文件基本是用于提供該節(jié)點(diǎn)下服務(wù)的(有些用于給自己調(diào)用,有些給其他進(jìn)程調(diào)用呼股,這個后期會具體說到)
  • 首先進(jìn)入的master.py 文件
# master.py
# coding:utf8
'''
Created on 2013-8-2
@author: lan (www.9miao.com)
'''
import subprocess,json,sys
from twisted.internet import reactor
from firefly.utils import  services
from firefly.distributed.root import PBRoot,BilateralFactory
from firefly.server.globalobject import GlobalObject
from twisted.web import vhost
from firefly.web.delayrequest import DelaySite
from twisted.python import log
from firefly.server.logobj import loogoo

reactor = reactor

MULTI_SERVER_MODE = 1
SINGLE_SERVER_MODE = 2
MASTER_SERVER_MODE = 3

class Master:
    """
    """
    
    def __init__(self):
        """
        """
        self.configpath = None 
        self.mainpath = None
        self.root = None
        self.webroot = None
        
    def config(self,configpath,mainpath):
        """
        """
        self.configpath = configpath
        self.mainpath = mainpath
        
    def masterapp(self):
        """
        """
        config = json.load(open(self.configpath,'r'))
        GlobalObject().json_config = config #這里的GlobalObject()是一個單例模式的對象耕魄,如果對于這一開發(fā)模式不了解的同學(xué)可以直接把GlobalObject()當(dāng)做一個全局變量
        mastercnf = config.get('master')
        rootport = mastercnf.get('rootport')
        webport = mastercnf.get('webport')
        masterlog = mastercnf.get('log')
        self.root = PBRoot() #創(chuàng)建分布式根節(jié)點(diǎn) 
        rootservice = services.Service("rootservice")#創(chuàng)建根節(jié)點(diǎn)服務(wù)
        self.root.addServiceChannel(rootservice)#將服務(wù)添加進(jìn)入根節(jié)點(diǎn)

        self.webroot = vhost.NameVirtualHost()# 創(chuàng)建web服務(wù)
        self.webroot.addHost('0.0.0.0', './')#添加web服務(wù)器ip

        GlobalObject().root = self.root #將節(jié)點(diǎn)信息放入全局變量中
        GlobalObject().webroot = self.webroot #將web節(jié)點(diǎn)放入全局變量
        if masterlog:
            log.addObserver(loogoo(masterlog))#添加日志觀察者
        log.startLogging(sys.stdout)
        import webapp
        import rootapp
        reactor.listenTCP(webport, DelaySite(self.webroot)) #web服務(wù)監(jiān)聽端口
        reactor.listenTCP(rootport, BilateralFactory(self.root)) #master服務(wù)監(jiān)聽端口
        
    def start(self):
        '''
        '''
        sys_args = sys.argv
        if len(sys_args)>2 and sys_args[1] == "single":
            server_name = sys_args[2]
            if server_name == "master":
                mode = MASTER_SERVER_MODE
            else:
                mode = SINGLE_SERVER_MODE
        else:
            mode = MULTI_SERVER_MODE
            server_name = ""
            
        if mode == MULTI_SERVER_MODE:
            self.masterapp()
            config = json.load(open(self.configpath,'r'))
            sersconf = config.get('servers')
            for sername in sersconf.keys():
                cmds = 'python %s %s %s'%(self.mainpath,sername,self.configpath)
                subprocess.Popen(cmds,shell=True)
            reactor.run()
        elif mode == SINGLE_SERVER_MODE:
            config = json.load(open(self.configpath,'r'))
            sername = server_name
            cmds = 'python %s %s %s'%(self.mainpath,sername,self.configpath)
            subprocess.Popen(cmds,shell=True)
        else:
            self.masterapp()
            reactor.run()

通過master源碼可以看出,軟件的運(yùn)行順序
1 創(chuàng)建分布式根節(jié)點(diǎn)服務(wù)
2 創(chuàng)建masterweb服務(wù)
3 通過多進(jìn)程的調(diào)用python ***.py 的方式 直接運(yùn)行分布式服務(wù)

  • rootapp
#coding:utf8
'''
Created on 2013-8-7

@author: lan (www.9miao.com)
'''
from firefly.server.globalobject import GlobalObject
from twisted.python import log

def _doChildConnect(name,transport):
    """
    當(dāng)server節(jié)點(diǎn)連接到master節(jié)點(diǎn)時觸發(fā)
    """
    server_config = GlobalObject().json_config.get('servers',{}).get(name,{})# 獲取名為name的server配置信息
    child_host = transport.broker.transport.client[0] # 保存連接進(jìn)來的子節(jié)點(diǎn)ip地址卖怜,若是服務(wù)器有固定ip屎开,則可直接寫入配置文件內(nèi)阐枣,無需保存
remoteport = server_config.get('remoteport',[]) # 獲取配置信息內(nèi)遠(yuǎn)程端口信息(一般為gate節(jié)點(diǎn)信息)
    root_list = [rootport.get('rootname') for rootport in remoteport]# 從配置文件內(nèi)提取遠(yuǎn)程端口host信息
    GlobalObject().remote_map[name] = {"host":child_host,"root_list":root_list}
    # 保存root信息到 節(jié)點(diǎn)連接池中這是一個全局變量(這里實際是一個單例模式實現(xiàn)的全局變量)
    
    # 到了這里马靠,我們目標(biāo)節(jié)點(diǎn)連接master節(jié)點(diǎn)部分已經(jīng)基本完成
    # 但是我們看過前面框架圖的都知道,master節(jié)點(diǎn)本身不參與工作蔼两,所以無論是gate甩鳄,game,還是net節(jié)點(diǎn)额划,只連入master本身是無法讓軟件正常運(yùn)行起來的妙啃,所以有了下面那兩部分代碼
    
    # GlobalObject().remote_map.items() 節(jié)點(diǎn)連接池,保存已經(jīng)連入的節(jié)點(diǎn)信息

    # 從節(jié)點(diǎn)連接池中選擇需要連接本次連入節(jié)點(diǎn)的節(jié)點(diǎn)俊戳,告知他連接 root節(jié)點(diǎn)揖赴。
    for servername,remote_list in GlobalObject().remote_map.items():
        remote_host = remote_list.get("host","")
        remote_name_host = remote_list.get("root_list","")
        if name in remote_name_host:
            GlobalObject().root.callChild(servername,"remote_connect",name,remote_host)

    # 查看本次連入的節(jié)點(diǎn) 是否有需要連接的root節(jié)點(diǎn)存在于 節(jié)點(diǎn)連接池中,如果有抑胎,通知他連接該節(jié)點(diǎn)
    master_node_list = GlobalObject().remote_map.keys()
    for root_name in root_list:
        if root_name in master_node_list:
            root_host = GlobalObject().remote_map[root_name]['host']
            GlobalObject().root.callChild(name,"remote_connect",root_name,root_host)
    

def _doChildLostConnect(childId):
    """
     # 當(dāng)節(jié)點(diǎn)連接斷開時觸發(fā)
    """
    try:
        # 將節(jié)點(diǎn)移出節(jié)點(diǎn)連接池
        del GlobalObject().remote_map[childId]
    except Exception,e:
        log.msg(str(e))

# 將以上方法綁定到 master 對象的 doChildConnect 以及doChildLostConnect 方法中
# GlobalObject().root 實際是一個工廠模型燥滑,他實現(xiàn)了兩個空方法  
# doChildConnect 
# doChildLostConnect
# 這兩個方法會在 連接產(chǎn)生,連接斷開時調(diào)用阿逃,單因為是空的铭拧,所以什么也不會做
# 所以需要在外部實現(xiàn)對應(yīng)的方法赃蛛,并替換工廠內(nèi)部的空方法,這樣搀菩,你的函數(shù)就會在指定時間內(nèi)觸發(fā)
GlobalObject().root.doChildConnect = _doChildConnect
GlobalObject().root.doChildLostConnect = _doChildLostConnect

這里的代碼看上去非常抽象呕臂,基本上可以理解成當(dāng) 子節(jié)點(diǎn)x 連接上master節(jié)點(diǎn)后,master節(jié)點(diǎn)通知子節(jié)點(diǎn)x連接他們需要連接的root節(jié)點(diǎn)肪跋,以及通知需要連接該 子節(jié)點(diǎn)x 的節(jié)點(diǎn)歧蒋,讓他們連接 子節(jié)點(diǎn)x ,這里有點(diǎn)像 爸爸到家了澎嚣,碰到了媽媽做好飯疏尿,媽媽通知爸爸叫爺爺來吃飯,同時讓兒子也來吃飯易桃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褥琐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晤郑,更是在濱河造成了極大的恐慌敌呈,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件造寝,死亡現(xiàn)場離奇詭異磕洪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诫龙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門析显,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人签赃,你說我怎么就攤上這事谷异。” “怎么了锦聊?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵歹嘹,是天一觀的道長。 經(jīng)常有香客問我孔庭,道長尺上,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任圆到,我火速辦了婚禮怎抛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芽淡。我一直安慰自己马绝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布吐绵。 她就那樣靜靜地躺著迹淌,像睡著了一般河绽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唉窃,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天耙饰,我揣著相機(jī)與錄音,去河邊找鬼纹份。 笑死苟跪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔓涧。 我是一名探鬼主播件已,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼元暴!你這毒婦竟也來了篷扩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤茉盏,失蹤者是張志新(化名)和其女友劉穎鉴未,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸠姨,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铜秆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了讶迁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片连茧。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖巍糯,靈堂內(nèi)的尸體忽然破棺而出啸驯,到底是詐尸還是另有隱情,我是刑警寧澤鳞贷,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布坯汤,位于F島的核電站虐唠,受9級特大地震影響搀愧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疆偿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一咱筛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杆故,春花似錦迅箩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拐揭。三九已至,卻和暖如春奕塑,著一層夾襖步出監(jiān)牢的瞬間堂污,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工龄砰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盟猖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓换棚,卻偏偏與公主長得像式镐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子固蚤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348