go語言遊戲服務端開發(四)——RPC機制

五邑隱俠,本名關健昌,12年遊戲生涯。 本教程以Go語言為例。

 

RPC指遠程方法調用,遊戲里引入RPC目的是降低跨進程交互的複雜度。
遊戲業務設計為多go routine,一個玩家一個go routine。遊戲里RPC客戶端阻塞式調用遠程(服務進程)方法,這樣處理的好處是跨進程交互的業務也可以按照單執行緒順序執行的思路實現。
RPC請求包由以下幾部分組成:標記(字元串,用於區分是哪類調用)、序列號(一次調用的唯一標記)、方法編號(用於映射調用的方法)、參數。
RPC響應包由以下幾部分組成:標記、序列號、方法編號、返回值。
type RpcRequest struct {
    Mark      []byte
    FuncNo    uint16
    SerialNo  uint16
    Params    []byte
}

type RpcResponse struct {
    Mark      []byte
    FuncNo    uint16
    SerialNo  uint16
    ReturnVal    []byte
}

 

RPC建立在P2P網路之上,在 P2pListener interface 的 OnP2pCall(p2p *P2pNet, pack *P2pPack) 實現方法,根據包頭前幾個位元組與 Mark 對比,如果一致則調用對應的RPC客戶端/服務端進行處理。
對於RPC服務,可以參考http服務,建立方法編號到處理方法的映射。
type RpcHandler func(params []byte) ([]byte, error)

type RpcServ struct {
    p2p               *P2pNet
    mark              []byte
    mapFuncNo2Handler map[uint16]RpcHandler
}

func (s *RpcServ) HandleFunc(funcNo uint16, handler func(params []byte) ([]byte, error)) {
    if handler == nil {
        return
    }
    s.mapFuncNo2Handler[funcNo] 
= handler }

 

當收到請求後,解析出funcNo和參數Params,調用對應的處理器進行處理。
handler, ok := s.mapFuncNo2Handler[rpc.FuncNo]
if !ok {
    return fmt.Errorf("no handler for funcNo %d", rpc.FuncNo)
}
returnVal, err :
= handler(rpc.Params)

 

RPC服務對請求的處理可以是非阻塞的。
一般只對RPC客戶端做成阻塞的,這樣在客戶端調用遠程方法時,客戶端會一直等待服務端返回才繼續往下執行,整個邏輯流程跟單執行緒執行一樣。
通過序列號SerialNo、方法號 FuncNo 雙重確認響應包對應於當前請求。
在go語言里,可以通過定義一個長度為1的 chan byte,請求發出後,讀取通道,如果還沒有收到響應就會阻塞,響應返回時往通道里寫個1,喚醒調用流程繼續執行。
func Wait(e chan byte) {
    <-e
}
func Signal(e chan byte) {
    e <- 1
}

 

調用方法的參數和返回值都是二進位,業務可以根據自己的需要用json或 protobuff 進行序列化/反序列化。
func (c *RpcClient) invoke(params []byte) ([]byte, error)

 

RPC調用介紹到這裡。接下來介紹下遊戲業務進程的實現。