美文网首页Java 杂谈互联网科技
go微服务框架go-micro深度学习 rpc方法调用过程详解

go微服务框架go-micro深度学习 rpc方法调用过程详解

作者: 90299fbffdea | 来源:发表于2019-02-18 20:53 被阅读1次

    摘要:上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取server的地址信息,就可以和服务建立连接,然后就可以进行通信了。

            go-micro 支持很多通信协议:http、tcp、grpc等,支持的编码方式也很多有json、protobuf、bytes、jsonrpc等。也可以根据自己的需要实现通信协议和编码方式。go-micro 默认的通信协议是http,默认的编码方式是protobuf,我就以默认的方式来分解他的具体实现。

    服务的启动

        go-micro在启动的时候会选择默认通信协议http和protobuf编码方式,但他是如何路由到具体方法的?在go-micro服务端启动的时候我们需要注册Handler,也就是我们具体实现结构体 ,如例子中注册方法时,我们调用的RegisterSayHandler方法

    // 注册 Handlerrpcapi.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 methodsform :=0; m < s.typ.NumMethod(); m++ {

            method := s.typ.Method(m)

                    // prepareMethod会把所有解析的信息返回来ifmt := prepareMethod(method); mt != nil {

                s.method[method.Name] = mt

            }

        }

            // .....

        // save handlerrouter.serviceMap[s.name] = s

        return nil

    }

        serviceMap里保存的就是反射后的信息,下图是我用goland的debug得到的保存信息 

        路由信息处理完后,主要的工作就已经完成了,然后注册服务并启动服务,启动的服务是一个http的服务,我们可以看一下http_transport.go里的代码 

      服务的简单流程图如下 ,选择通信协议和编码方式->注册服务方法->启动服务并注册服务信息

    客户端调用服务方法

         客户端在启动的时候也要选择默认的通信协议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...)

      iferr != nil {

          return nil, err

      }

      returnout, nil

    }

        他的实现方式是封装request,然后调用服务方法。这个request 是非常重要的: 

    func newRequest(service, endpointstring, requestinterface{}, contentTypestring, reqOpts ...RequestOption) Request {

        var opts RequestOptions

        for_, o := range reqOpts {

            o(&opts)

        }

        // set the content-type specifiediflen(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或者从缓存获取服务器信息,连接服务器,发送数据。 

          简单流程图如下

         client:封装参数-> 编码数据->连接服务->发送数据->接收返回数据,并解码。

         service: 接收数据->解码数据,找到相应的实例和方法,利用反射调用具体方法->编码返数据->发送给客户端。

    服务端处理请求

         当服务端监接收到数据后,从http的Request里的Header中读取到相应的信息:编码方式,endpoint,请求的数据,由路由器进行对比和匹配找到保存的反射信息,利用反射把请求的数据根据相应的编码方式进行解码,再利用反射调用具体的方法,处理完把返回数据进行编码,组织一个http.Response传输给用户,客户端接收到数据后进行解码读取数据。 rpc_router.go里的call方法就是具体的调用过程方法,有时间大家可以读一下。

    作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要!

    这是一个我的QQ群架构华山论剑:836442475,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

    相关文章

      网友评论

        本文标题:go微服务框架go-micro深度学习 rpc方法调用过程详解

        本文链接:https://www.haomeiwen.com/subject/lshzeqtx.html