上一篇帖子go微服務框架go-micro深度學習(三) Registry服務的註冊和發現詳細解釋了go-micro是如何作服務註冊和發如今,服務端註冊server信息,client獲取server的地址信息,就能夠和服務創建鏈接,而後就能夠進行通訊了。這篇帖子詳細說一下,go-micro的通訊協議、編碼,和具體服務方法的調用過程是如何實現的,文中的代碼仍是我github上的例子: gomicrorpchtml
go-micro 支持不少通訊協議:http、tcp、grpc等,支持的編碼方式也不少有json、protobuf、bytes、jsonrpc等。也能夠根據本身的須要實現通訊協議和編碼方式。go-micro 默認的通訊協議是http,默認的編碼方式是protobuf,我就以默認的方式來分解他的具體實現。git
go-micro在啓動的時候會選擇默認通訊協議http和protobuf編碼方式,但他是如何路由到具體方法的?在go-micro服務端啓動的時候咱們須要註冊Handler,也就是咱們具體實現結構體 ,如例子中註冊方法時,咱們調用的RegisterSayHandler方法github
// 註冊 Handler rpcapi.RegisterSayHandler(service.Server(), new(handler.Say))
這個方法內部的體實現主要是利用了反射的力量,註冊的對象是實現了rpc接口的方法,如咱們的Say實現了SayHandler。go-micro默認的router會利用反射把Say對象的信息徹底提取出來,解析出結構體內的方法及方法的參數,保存到一個map內-> map[結構體名稱][方法信息集合]json
具體的實如今rpc_router.go裏router的Handle(Handler)方法,組織完成後map的是下圖這樣,保存了不少反射信息,用以未來調用。 api
下面是這個方法的主要代碼,刪除了一些,很但願你們讀一下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獲得的保存信息 服務器
路由信息處理完後,主要的工做就已經完成了,而後註冊服務並啓動服務,啓動的服務是一個http的服務,咱們能夠看一下http_transport.go裏的代碼 框架
服務的簡單流程圖以下 ,選擇通訊協議和編碼方式->註冊服務方法->啓動服務並註冊服務信息tcp
客戶端在啓動的時候也要選擇默認的通訊協議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 }
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, } }
簡單流程圖以下
client:封裝參數-> 編碼數據->鏈接服務->發送數據->接收返回數據,並解碼。
service: 接收數據->解碼數據,找到相應的實例和方法,利用反射調用具體方法->編碼返數據->發送給客戶端。
當服務端監接收到數據後,從http的Request裏的Header中讀取到相應的信息:編碼方式,endpoint,請求的數據,由路由器進行對比和匹配找到保存的反射信息,利用反射把請求的數據根據相應的編碼方式進行解碼,再利用反射調用具體的方法,處理完把返回數據進行編碼,組織一個http.Response傳輸給用戶,客戶端接收到數據後進行解碼讀取數據。 rpc_router.go裏的call方法就是具體的調用過程方法,有時間你們能夠讀一下。