天天看點

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

      上一篇文章go微服務架構go-micro深度學習(三) Registry服務的注冊和發現詳細解釋了go-micro是如何做服務注冊和發現在,服務端注冊server資訊,client擷取server的位址資訊,就可以和服務建立連接配接,然後就可以進行通信了。這篇文章詳細說一下,go-micro的通信協定、編碼,和具體服務方法的調用過程是如何實作的,文中的代碼還是我github上的例子: gomicrorpc

   go-micro 支援很多通信協定:http、tcp、grpc等,支援的編碼方式也很多有json、protobuf、bytes、jsonrpc等。也可以根據自己的需要實作通信協定和編碼方式。go-micro 預設的通信協定是http,預設的編碼方式是protobuf,我就以預設的方式來分解他的具體實作。

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

服務的啟動

    go-micro在啟動的時候會選擇預設通信協定http和protobuf編碼方式,但他是如何路由到具體方法的?在go-micro服務端啟動的時候我們需要注冊Handler,也就是我們具體實作結構體 ,如例子中注冊方法時,我們調用的RegisterSayHandler方法

// 注冊 Handler
rpcapi.RegisterSayHandler(service.Server(), new(handler.Say))      

    這個方法内部的體實作主要是利用了反射的力量,注冊的對象是實作了rpc接口的方法,如我們的Say實作了SayHandler。go-micro預設的router會利用反射把Say對象的資訊完全提取出來,解析出結構體内的方法及方法的參數,儲存到一個map内-> map[結構體名稱][方法資訊集合]

具體的實作在rpc_router.go裡router的Handle(Handler)方法,組織完成後map的是下圖這樣,儲存了很多反射資訊,用以将來調用。 

     下面是這個方法的主要代碼,删除了一些,很希望大家讀一下rpc_router.go裡面的代碼,prepareMethod方法是具體利用反射提取資訊的方法。

func (router *router) Handle(h Handler) error {
    router.mu.Lock()
    defer router.mu.Unlock()
    // ....

    rcvr := h.Handler()
    s := new(service)
    s.typ = reflect.TypeOf(rcvr)
    s.rcvr = reflect.ValueOf(rcvr)

    // check name
        // ....
    s.name = h.Name()
    s.method = make(map[string]*methodType)

    // Install the methods
    for m := 0; m < s.typ.NumMethod(); m++ {
        method := s.typ.Method(m)
                // prepareMethod會把所有解析的資訊傳回來
        if mt := prepareMethod(method); mt != nil {
            s.method[method.Name] = mt
        }
    }
        // .....
    // save handler
    router.serviceMap[s.name] = s
    return nil
}      

    serviceMap裡儲存的就是反射後的資訊,下圖是我用goland的debug得到的儲存資訊 

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

    路由資訊處理完後,主要的工作就已經完成了,然後注冊服務并啟動服務,啟動的服務是一個http的服務,我們可以看一下http_transport.go裡的代碼 

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

  服務的簡單流程圖如下 ,選擇通信協定和編碼方式->注冊服務方法->啟動服務并注冊服務資訊

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

用戶端調用服務方法

     用戶端在啟動的時候也要選擇預設的通信協定http,和protobuf編碼。用戶端在調用rpc方法的時候如: 

rsp, err := client.Hello(context.Background(), &model.SayParam{Msg: "hello server"})      

     go-micro為我們自動生成的rpcapi.micro.go裡我們可以看一上Hello的具體實作,沒有幾行代碼,但内部還是做了很多工作

func (c *sayService) Hello(ctx context.Context, in *model.SayParam, opts ...client.CallOption) (*model.SayResponse, error) {
   req := c.c.NewRequest(c.name, "Say.Hello", in)
   out := new(model.SayResponse)
   err := c.c.Call(ctx, req, out, opts...)
   if err != nil {
      return nil, err
   }
   return out, nil
}      

    他的實作方式是封裝request,然後調用服務方法。這個request 是非常重要的: 

func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
    var opts RequestOptions

    for _, o := range reqOpts {
        o(&opts)
    }

    // set the content-type specified
    if len(opts.ContentType) > 0 {
        contentType = opts.ContentType
    }

    return &rpcRequest{
        service:     service,
        method:      endpoint,
        endpoint:    endpoint,
        body:        request,
        contentType: contentType,
        opts:        opts,
    }
}      

    這個方法傳回一個rpcRequest裡面包含了詳細的調用資訊,servicec:服務名,method和endpoint目前是一樣的是方法名這裡是Say.Hello,contentType是protobuf,body 是具體的資訊,也要要進行編碼的内容,使用protobuf進行編碼,然後會把這些資訊放到一個http.Request裡,再從Register或者從緩存擷取伺服器資訊,連接配接伺服器,發送資料。 

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

      簡單流程圖如下

     client:封裝參數-> 編碼資料->連接配接服務->發送資料->接收傳回資料,并解碼。

     service: 接收資料->解碼資料,找到相應的執行個體和方法,利用反射調用具體方法->編碼返資料->發送給用戶端。

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

服務端處理請求

     當服務端監接收到資料後,從http的Request裡的Header中讀取到相應的資訊:編碼方式,endpoint,請求的資料,由路由器進行對比和比對找到儲存的反射資訊,利用反射把請求的資料根據相應的編碼方式進行解碼,再利用反射調用具體的方法,處理完把傳回資料進行編碼,組織一個http.Response傳輸給使用者,用戶端接收到資料後進行解碼讀取資料。 rpc_router.go裡的call方法就是具體的調用過程方法,有時間大家可以讀一下。

go微服務架構go-micro深度學習(四) rpc方法調用過程詳解

作者:李鵬

出處:http://www.cnblogs.com/li-peng/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。