Leaf游戲服務(wù)器簡(jiǎn)析(二)之ChanRPC

ChanRPC實(shí)現(xiàn)模塊(Module)goroutine間的通信

為了進(jìn)一步分析Leaf游戲服務(wù)器,我們需要了解Leaf的ChanRPC

Leaf中每個(gè)模塊在獨(dú)立的goroutine間運(yùn)行,Leaf提供了基于channel的RPC機(jī)制來(lái)實(shí)現(xiàn)模塊間的相互通信

chanrpc功能的實(shí)現(xiàn)(Server為例)

// leaf\chanrpc\chanrpc.go
type Server struct {
    // id -> function
    //
    // function:
    // func(args []interface{})
    // func(args []interface{}) interface{}
    // func(args []interface{}) []interface{}
    functions map[interface{}]interface{}
    ChanCall  chan *CallInfo
}

Server類型的變量會(huì)維護(hù)一個(gè)函數(shù)映射關(guān)系functions,通過(guò)接受到的消息去調(diào)用相應(yīng)注冊(cè)的函數(shù),而ChanCall通道則用來(lái)搭建通道間的信息傳遞橋梁

ServerRegister方法實(shí)現(xiàn)了對(duì)functions的注冊(cè)

// leaf\chanrpc\chanrpc.go
// you must call the function before calling Open and Go
func (s *Server) Register(id interface{}, f interface{}) {
    switch f.(type) {
    case func([]interface{}):
    case func([]interface{}) interface{}:
    case func([]interface{}) []interface{}:
    default:
        panic(fmt.Sprintf("function id %v: definition of function is invalid", id))
    }

    if _, ok := s.functions[id]; ok {
        panic(fmt.Sprintf("function id %v: already registered", id))
    }

    s.functions[id] = f
}

ChanCall參數(shù)則將在某個(gè)地方進(jìn)行通道數(shù)據(jù)的讀取和寫入,來(lái)達(dá)到通道間通信的功能
例如使用GO調(diào)用,往通道里寫入數(shù)據(jù),數(shù)據(jù)包括要進(jìn)行回調(diào)的functions中的函數(shù)及其參數(shù)

// leaf\chanrpc\chanrpc.go
func (s *Server) Go(id interface{}, args ...interface{}) {
    // 拿到要調(diào)用的函數(shù)
    f := s.functions[id]
    if f == nil {
        return
    }

    defer func() {
        recover()
    }()
    
    // 往通道里寫入數(shù)據(jù)   函數(shù) 和 參數(shù)
    s.ChanCall <- &CallInfo{
        f:    f,
        args: args,
    }
}

而在寫入數(shù)據(jù)之前,必定有在程序的某個(gè)地方通過(guò)這個(gè)通道等待接收數(shù)據(jù),LeafServer中在進(jìn)入通過(guò)Skeleton模塊建立的Module的Run()生命周期中,在模塊的goroutine中就會(huì)等待接收數(shù)據(jù),接收到數(shù)據(jù)后執(zhí)行Exec()來(lái)進(jìn)行處理,Skeleton相關(guān)內(nèi)容將在后面的文章進(jìn)行簡(jiǎn)析

// leaf\module\skeleton.go
func (s *Skeleton) Run(closeSig chan bool) {
    for {
        select {
        case <-closeSig:
            s.commandServer.Close()
            s.server.Close()
            for !s.g.Idle() || !s.client.Idle() {
                s.g.Close()
                s.client.Close()
            }
            return
        case ri := <-s.client.ChanAsynRet:
            s.client.Cb(ri)

        // 等待來(lái)自通道的數(shù)據(jù)
        case ci := <-s.server.ChanCall:
            s.server.Exec(ci)

        case ci := <-s.commandServer.ChanCall:
            s.commandServer.Exec(ci)
        case cb := <-s.g.ChanCb:
            s.g.Cb(cb)
        case t := <-s.dispatcher.ChanTimer:
            t.Cb()
        }
    }
}

Exec()調(diào)用相應(yīng)函數(shù)

// leaf\chanrpc\chanrpc.go

func (s *Server) Exec(ci *CallInfo) {
    err := s.exec(ci)
    if err != nil {
        log.Error("%v", err)
    }
}

func (s *Server) exec(ci *CallInfo) (err error) {
    defer func() {
        if r := recover(); r != nil {
            if conf.LenStackBuf > 0 {
                buf := make([]byte, conf.LenStackBuf)
                l := runtime.Stack(buf, false)
                err = fmt.Errorf("%v: %s", r, buf[:l])
            } else {
                err = fmt.Errorf("%v", r)
            }
            s.ret(ci, &RetInfo{err: fmt.Errorf("%v", r)})
        }
    }()
    // execute
    switch ci.f.(type) {
    case func([]interface{}):
        ci.f.(func([]interface{}))(ci.args)
        return s.ret(ci, &RetInfo{})
    case func([]interface{}) interface{}:
        ret := ci.f.(func([]interface{}) interface{})(ci.args)
        return s.ret(ci, &RetInfo{ret: ret})
    case func([]interface{}) []interface{}:
        ret := ci.f.(func([]interface{}) []interface{})(ci.args)
        return s.ret(ci, &RetInfo{ret: ret})
    }
    panic("bug")
}

LeafServer中的chanrpc使用舉例

簡(jiǎn)單講解LeafServer中g(shù)ate.Module與game.Module的通信,下文中的目錄server為L(zhǎng)eafServer項(xiàng)目根目錄

首先程序一開始在game模塊中注冊(cè)了相應(yīng)的ChanRPC消息和響應(yīng)函數(shù)(Go pkg里的init()函數(shù)會(huì)在main()之前執(zhí)行),game模塊注冊(cè)了NewAgent 和CloseAgent 兩個(gè)ChanRPC,分別執(zhí)行用戶建立連接和斷開鏈接的相關(guān)邏輯

// server\game\internal\chanrpc.go
func init() {
    skeleton.RegisterChanRPC("NewAgent", rpcNewAgent)
    skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent)
}

func rpcNewAgent(args []interface{}) {
    a := args[0].(gate.Agent)
    _ = a
}

func rpcCloseAgent(args []interface{}) {
    a := args[0].(gate.Agent)
    _ = a
}

// skeleton.RegisterChanRPC()方法調(diào)用的是上文提到的Server.Register
// leaf\module\skeleton.go
func (s *Skeleton) RegisterChanRPC(id interface{}, f interface{}) {
    if s.ChanRPCServer == nil {
        panic("invalid ChanRPCServer")
    }
    // 注冊(cè)
    s.server.Register(id, f)
}

注冊(cè)完ChanRPC后,在程序運(yùn)行起來(lái)后,會(huì)按照Module生命周期進(jìn)到如前文所說(shuō)的Run中等待數(shù)據(jù)被寫入通道

// leaf\module\skeleton.go
func (s *Skeleton) Run(closeSig chan bool) {
    for {
        select {
        // ...
        // 等待來(lái)自通道的數(shù)據(jù)
        case ci := <-s.server.ChanCall:
            s.server.Exec(ci)
        //...
        }
    }
}

gate模塊中當(dāng)用戶連接時(shí)將調(diào)用Server.Go函數(shù)像game模塊發(fā)送消息

// leaf\gate\gate.go
gate.AgentChanRPC.Go("NewAgent", a)

// leaf\chanrpc\chanrpc.go
func (s *Server) Go(id interface{}, args ...interface{}) {
    // ...
    // 往通道里寫入數(shù)據(jù)   函數(shù) 和 參數(shù)
    s.ChanCall <- &CallInfo{
        f:    f,
        args: args,
    }
}

調(diào)用了Server.GO后,在Run()里面等待的通道就可以拿到消息,從而執(zhí)行相應(yīng)的處理函數(shù)了
而對(duì)于gate.AgentChanRPC則在gate模塊的初始化中就指向了game模塊的ChanRPC:

// server\gate\internal\module.go
func (m *Module) OnInit() {
    m.Gate = &gate.Gate{
        // ...
        AgentChanRPC:    game.ChanRPC,  //注冊(cè)為game的ChanRPC,于是就可以通過(guò)gate.AgentChanRPC.Go("NewAgent", a)和game模塊通信
    }
}

小結(jié)

本文僅僅只是簡(jiǎn)單梳理了一下ChanRPC通信的流程,回頭看整個(gè)過(guò)程其實(shí)很清晰,程序初始化的時(shí)候模塊注冊(cè)好需要的ChanRPC,在生命周期Run()里等待模塊的ChanRPC里的通道的信號(hào).其他模塊往等待信號(hào)的通道里發(fā)送內(nèi)容,等待信號(hào)的模塊拿到內(nèi)容后調(diào)用注冊(cè)好的函數(shù).拋磚引玉了一下,歡迎交流討論

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末如筛,一起剝皮案震驚了整個(gè)濱河市疙赠,隨后出現(xiàn)的幾起案子板祝,更是在濱河造成了極大的恐慌怔匣,老刑警劉巖界酒,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奇瘦,死亡現(xiàn)場(chǎng)離奇詭異蜂怎,居然都是意外死亡楼吃,警方通過(guò)查閱死者的電腦和手機(jī)演痒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門亲轨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鸟顺,你說(shuō)我怎么就攤上這事惦蚊。” “怎么了讯嫂?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蹦锋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我欧芽,道長(zhǎng)莉掂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任千扔,我火速辦了婚禮憎妙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘曲楚。我一直安慰自己厘唾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布龙誊。 她就那樣靜靜地躺著抚垃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讯柔,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天抡蛙,我揣著相機(jī)與錄音,去河邊找鬼魂迄。 笑死粗截,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捣炬。 我是一名探鬼主播熊昌,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼湿酸!你這毒婦竟也來(lái)了婿屹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤推溃,失蹤者是張志新(化名)和其女友劉穎昂利,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铁坎,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜂奸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硬萍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扩所。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖朴乖,靈堂內(nèi)的尸體忽然破棺而出祖屏,到底是詐尸還是另有隱情,我是刑警寧澤买羞,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布袁勺,位于F島的核電站,受9級(jí)特大地震影響哩都,放射性物質(zhì)發(fā)生泄漏魁兼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一漠嵌、第九天 我趴在偏房一處隱蔽的房頂上張望咐汞。 院中可真熱鬧,春花似錦儒鹿、人聲如沸化撕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)植阴。三九已至蟹瘾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掠手,已是汗流浹背憾朴。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喷鸽,地道東北人众雷。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像做祝,于是被迫代替她去往敵國(guó)和親砾省。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容