RedRabbit——基於BrokerPattern服務器框架


RedRabbit

經典網游服務器架構

該圖省略了專門用途的dbserverguildserver等用於專門功能的server,該架構的優點有:

  • l LoginGate相當於DNS,可以動態的保證GameGate之間負載均衡。
  • l 由於Clientt的邏輯操作都是由GameServer處理的,而Client的消息請求都被GameGate轉發到GameServer上,所以在不同的GameGate上的client仍能出現在相同的場景里。若在不同的場景,又可以將其分布在不公的GameServer處理,從而實現了GameServerScalability
  • l GameServer一般是由C++與腳本結合實現的。由於數據都是在內存中處理而且大部分的IO操作(網絡、數據庫等)都被異步化,所以保證了非常高的實時性。

缺點是:

  • l 各個節點之間通過socket進行異步通信,測試過程叫復雜。
  • l 各個節點往往都需要交互,這時就涉及到了誰連誰的問題,理解和設計架構的網絡拓撲也變得不太容易,相應的配置也會叫繁瑣,排錯的難度也較大。
  • l GameServer由於是C++主語言實現,不免會涉及到崩潰和內存泄露問題,采用C++與腳本結合很大程度上緩解了這個問題,實際上越來越多的邏輯操作都是放到腳本中實現。
  • l 由於該架構必須正確的配置連接關系,否則不能正常工作,對於運維而言也並不輕松。

 

討論完經典網游的服務器架構,今天的主題也呼之欲出了,但在此之前,先說一下該架構的核心思想,如果你讀過《面向模式的軟件架構.4,分布式計算的模式語言》你也許想到了BrokerPattern,其核心思想是通過Broker代理層,促使Server的位置對於Client保持透明,client通過Broker找到對應的Server處理請求,Serverr是如何分布的、數量多少,Client都不受影響。Broker可以存在兩種模式,一種是類似於DNS提供的LookUp服務,它只是幫助Client定位到Server的位置,Client直接連接到Serverr進行通信。LoginGate扮演的就是這種Broker。另外一種Broker直接將Client請求投遞給ServerrGameGate就是扮演的這種Broker。總的來說BrokerPattern中,Broker具有如下功能:

  • l LookUp服務,幫助Client定位Server
  • l Route服務,實現ClientServer之間的消息轉發
  • l 注冊服務,Server必須要注冊到Broker上這樣Broker才能提供LookUpRoute功能。

BrokerPattern示意圖:

 

 

所以今天的主題是如何利用BrokerPattern構建實時的服務器框架。

RedRabbit

目標:

  • l 節點之間通信采用異步消息、回調模式
  • l Server必須很容易注冊到Broker
  • l C++/EPOOL實現網絡通信,保證實時性,支持邏輯層python實現,支持熱更新
  • l 該框架能夠容易的構建單個區組的構架
  • l 該框架支持跨區組通信,這也是Broker模式的優勢,節點之間通信不需要知道對方的位置,只需要知道對方的名稱

這個框架的名字叫RedRabbit

FFRPC

首先介紹RedRabbit的通信組件ffrpcffrpc中有如下5種角色:

  • l BrokerMaster,負責管理所有的BrokerSlave,所有Slave需要注冊到BrokerMaster上,BrokerMaster同步所有信息給所有節點。
  • l BrokerSlave負責轉發ClientService之間的消息。
  • l Client為調用Service接口的一方,它通過BrokerService通信,Client不知道Service的具體位置,它只是知道當前與之通信的Service名稱。
  • l Service提供給Client調用的接口,並把接口注冊到Broker上,Service若調用了其他的Service的接口,則相對於其他Service其為Client角色。
  • l BrokerBridge負責橋接各個brokerMaster,每一個BrokerMaster負責一組服務,BrokerBridge使Client調用其他組接口和調用本組的接口一樣容易,因為只需要指定對方服務名稱即可。

各個角色示意圖:

 

使用FFRPC實現的Echo服務實例代碼:

http://www.cnblogs.com/zhiranok/archive/2013/06/06/ffrpc.html

RedRabbit中的其他組件GateScene

Gate

外網接入的client有些特殊,需要一定的安全處理。Gate是專門用於接入外部Client的組件。Gate的作用有:

  • l Client的第一個消息必須為驗證消息,Gate 並沒有驗證Client的能力,它調用Scene@0的接口處理
  • l Scene@0通過驗證后將Client將被分配唯一的SessionId
  • l Client的所有消息都被Gate轉發到對應的Scene上,Scene可以控制Gate接口切換某個Client到其他Scene
  • l Gate提供轉發消息、多播、廣播、斷開連接等接口公scene調用。

需要特別指出的是,GateScene只是RedRabbit的組件,RedRabbit通過制定不同的啟動參數來確定開啟哪些組件。示例:

  • l ./app_redrabbit -gate gate@0 -broker tcp://127.0.0.1:10241 -gate_listen tcp://121.199.21.238:10242
  • l -gate 表示gate的名稱,scene通過名稱調用其接口
  • l -gate_listen表示gate監聽的ipport
  • l -brkoker表示作為BrokerMaster啟動,一組服務中必須有一個BrokerMaster,如果BrokerClientService在同一進程中,Broker專門做了優化,消息會直接從內存間實現傳遞,避免了網絡轉發的開銷。

Scene

RedRabbit中的所有Service都是運行在Scene組件之下的。Scene提供了通用的接口,可以和Gate和其他Scene通信,並把接口導入到了python中。Scene接收的Client的請求都交由Python處理,所以可以用Scene+Python實現GameServerDbServer等各種專用的服務器。Scene組件提供的功能有:

  • l 驗證Client有效性,scene@0必須提供此接口
  • l 處理Client Enter消息,Scene第一次進入該Scene,觸發此事件
  • l 處理Client Offline消息,Client下線,觸發此事件
  • l Scene提供轉發、多播、廣播、關閉連接等接口給python
  • l Scene提供定時器接口給python
  • l Scene提供異步操作MysqlSqlite的接口,采用異步加回調,從而避免阻塞主線程
  • l Scene提供了一套消息派發框架,支持clientpython通信的協議包括jsonthriftprotobuf

使用RedRabbit構建的聊天室demo示例:

http://ffown.sinaapp.com/flash/

修改名稱,點擊flash的連接按鈕,進入聊天室發消息,右側的python腳本為服務器python的實現,修改右側腳本點保存按鈕,在flash中輸入reload即可實現熱更新!!!!

該聊天室服務器啟動的參數是:

./app_redrabbit -gate gate@0 -broker tcp://127.0.0.1:10241 -gate_listen tcp://121.199.21.238:10242 -python_path ./ -scene scene@0

該示例中把gatescene啟動到了一個服務器程序上,實際上通過調整參數,二者可以啟動到不同進程中,RedRabbit通過參數開啟組件,而組件之間是通過Broker建立聯系的。

對應的python代碼:

 

# coding=UTF-8
import os
import time
import ffext

def GetNowTime():
    return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))

class player_mgr_t(object):
    def __init__(self):
        self.all_players = {}
    def get(self, session_id_):
        return self.all_players.get(session_id_)
    def remove(self, session_id_):
        del  self.all_players[session_id_]
    def add(self, session_id_, player):
        self.all_players[session_id_] = player
    def size(self):
        return len(self.all_players)
    def idlist(self):
        return self.all_players.keys()

class player_t(object):
    def __init__(self, session_id_):
        self.session_id = session_id_;
    def id():
        return self.session_id

#這個修飾器的意思是注冊process_chat函數接收cmd=1的消息
@ffext.session_call(1)
def process_chat(session_id, msg):
    content = msg[0]
    if content == 'reload':
        os.system('./update_code.sh')
        ret = ffext.reload('main')#重載此腳本
        ffext.broadcast_msg_session(1, '<b><font color="#ff0000"> main.py已完成重載'\
                                       '%s</font></b>'%(str(ret)))
        return

    print("process_chat session_id=%s content=%s"%(session_id, content))

    ret = '<font color="#008000">[%s %s]:</font>%s'%(session_id, GetNowTime(), content)
    ffext.broadcast_msg_session(1, ret)


#這個修飾器的意思是注冊下面函數處理驗證client賬號密碼,
#session_key為賬號密碼組合體,client第一個包必為登陸包
@ffext.session_verify_callback
def my_session_verify(session_key, online_time, ip, gate_name):
    return [session_key]#需要返回數組,驗證成功,第一個元素為分配的id,
                        #第二個元素可以不設置,若設置gate會返回給client,login gate的時候
                        #需要第二個元素返回分配的game gate

#此修飾器的作用是注冊下面函數處理用戶下線 
@ffext.session_offline_callback
def my_session_offline(session_id, online_time):
    content = '<font color="#ff0000">[%s %s] offline </font>'%(session_id, GetNowTime())
    ffext.broadcast_msg_session(1, content)
    ffext.singleton(player_mgr_t).remove(session_id)
    ffext.broadcast_msg_session(1, '<font color="#ff0000">當前在線:</font>')
    ffext.broadcast_msg_session(1, ffext.singleton(player_mgr_t).idlist())

#此修飾器的作用是注冊下面函數處理client切換到此場景服務器
@ffext.session_enter_callback
def my_session_enter(session_id, from_scene, extra_data):
    #單播接口
    ffext.send_msg_session(session_id, 1, '<font color="#ff0000">測試單播接口!歡迎你!'\
                                             '</font>')
    content = '<font color="#ff0000">[%s %s] online </font>'%(session_id, GetNowTime())
    ffext.broadcast_msg_session(1, content)
    player = player_t(session_id)
    ffext.singleton(player_mgr_t).add(session_id, player)
    ffext.broadcast_msg_session(1, '<font color="#ff0000">當前在線:</font>')
    ffext.broadcast_msg_session(1, ffext.singleton(player_mgr_t).idlist())

print("loading.......")                                                        

 

 

 

總結:

  • l Ffrpc是基於BrokerPattern思想實現的異步消息+回調通訊庫。
  • l 使用python構建實時服務器完全可以做到,在一些頁游和手游項目尤其適合。確保高實時性的建議一是把數據在內存中操作,二是io操作異步化。
  • l RedRabbit支持ClientPython的通信協議有Jsonthriftprotobuf。我個人最喜歡thrift
  • l RedRabbit支持跨區組通信,通過BrokerBridgeGroupAGroupBBrokerMaster連通起來。示例:

啟動BrokerBridge

./app_redrabbit  -broker tcp://127.0.0.1:10241

啟動GroupABrokerMaster:

./app_redrabbit  -broker tcp://127.0.0.1:10242 -bridge_broker GroupA@tcp://127.0.0.1:10241

啟動GroupBBrokerMaster:

./app_redrabbit  -broker tcp://127.0.0.1:10242 -bridge_broker GroupB@tcp://127.0.0.1:10241

GroupApython中就可以這樣調用GroupB的接口:

Ffext.bridge_call(‘GroupB’, cmd, msg, callback)

 項目源碼:

https://github.com/fanchy/RedRabbit

TODO:

構建跨服的demo示例下一篇。

 

更多精彩文章 http://h2cloud.org


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM