文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Golang原生rpc的原理是什么

2023-06-29 21:03

关注

这篇文章主要讲解了“Golang原生rpc的原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang原生rpc的原理是什么”吧!

创建rpc接口,需要几个条件

rpc服务原理分析

server端

服务注册 通过反射处理,将接口存入到map中,进行调用 注册服务两个方法

func Register (rcvr interface{}) error {}func RegisterName (rcvr interface{} , name string) error {}//指定注册的名称

注册方法的源代码解读 首先,无论是Register还是RegisterName底层代码都是调用register方法,进行服务注册。 server.go register方法解读

func (server *Server) register(rcvr interface{}, name string, useName bool) error {//创建一个service实例s := new(service)s.typ = reflect.TypeOf(rcvr)s.rcvr = reflect.ValueOf(rcvr)sname := reflect.Indirect(s.rcvr).Type().Name()//如果服务名为空,则使用默认的服务名if useName {sname = name}if sname == "" {s := "rpc.Register: no service name for type " + s.typ.String()log.Print(s)return errors.New(s)}//判断方法名是否暴漏的,如果方法名不是暴露的,则会导致调用不成功,所以返回falseif !token.IsExported(sname) && !useName {s := "rpc.Register: type " + sname + " is not exported"log.Print(s)return errors.New(s)}s.name = sname// Install the methods//调用suitableMethods函数,进行返回接口,在suitableMethods中判断方法是否符合作为rpc接口的条件,如果符合,则进行添加到services中s.method = suitableMethods(s.typ, true)if len(s.method) == 0 {str := ""// To help the user, see if a pointer receiver would work.//如果方法绑定到结构体的地址上,使用reflect.TypeOf()是不会发现方法的,所以也要进行查找绑定到结构体地址上的方法method := suitableMethods(reflect.PtrTo(s.typ), false)if len(method) != 0 {str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"} else {str = "rpc.Register: type " + sname + " has no exported methods of suitable type"}log.Print(str)return errors.New(str)}//判断服务接口是否已经注册。if _, dup := server.serviceMap.LoadOrStore(sname, s); dup {return errors.New("rpc: service already defined: " + sname)}return nil}

suitableMethod方法解读

func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {//创建一个方法的切片methods := make(map[string]*methodType)for m := 0; m < typ.NumMethod(); m++ {method := typ.Method(m)mtype := method.Typemname := method.Name// Method must be exported.if method.PkgPath != "" {continue}// Method needs three ins: receiver, *args, *reply.//如果传入的参数,不为三个,则会报错,这里为什么是三个?//golang方法体中默认传入结构体实例,所以request,*response,结构体实例一共三个参数if mtype.NumIn() != 3 {if reportErr {log.Printf("rpc.Register: method %q has %d input parameters; needs exactly three\n", mname, mtype.NumIn())}continue}// First arg need not be a pointer.argType := mtype.In(1)if !isExportedOrBuiltinType(argType) {if reportErr {log.Printf("rpc.Register: argument type of method %q is not exported: %q\n", mname, argType)}continue}// Second arg must be a pointer.//判断第二个参数是否为指针,如果不为指针,则返回false。replyType := mtype.In(2)if replyType.Kind() != reflect.Ptr {if reportErr {log.Printf("rpc.Register: reply type of method %q is not a pointer: %q\n", mname, replyType)}continue}// Reply type must be exported.if !isExportedOrBuiltinType(replyType) {if reportErr {log.Printf("rpc.Register: reply type of method %q is not exported: %q\n", mname, replyType)}continue}// Method needs one out.//返回结果是否为一个值,且为errorif mtype.NumOut() != 1 {if reportErr {log.Printf("rpc.Register: method %q has %d output parameters; needs exactly one\n", mname, mtype.NumOut())}continue}// The return type of the method must be error.if returnType := mtype.Out(0); returnType != typeOfError {if reportErr {log.Printf("rpc.Register: return type of method %q is %q, must be error\n", mname, returnType)}continue}//将接口加入servicemethods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}}return methods}

接收到请求后会不断的解析请求 解析请求的两个方法 readRequestHeader

func (server *Server) readRequestHeader(codec ServerCodec) (svc *service, mtype *methodType, req *Request, keepReading bool, err error) {// Grab the request header.//接收到请求,对请求进行编码req = server.getRequest()err = codec.ReadRequestHeader(req)if err != nil {req = nilif err == io.EOF || err == io.ErrUnexpectedEOF {return}err = errors.New("rpc: server cannot decode request: " + err.Error())return}// We read the header successfully. If we see an error now,// we can still recover and move on to the next request.keepReading = true//编码后的请求,进行间隔,所以只要进行将.的左右两边的数据进行分割,就能解码dot := strings.LastIndex(req.ServiceMethod, ".")if dot < 0 {err = errors.New("rpc: service/method request ill-formed: " + req.ServiceMethod)return}serviceName := req.ServiceMethod[:dot]methodName := req.ServiceMethod[dot+1:]// Look up the request.svci, ok := server.serviceMap.Load(serviceName)if !ok {err = errors.New("rpc: can't find service " + req.ServiceMethod)return}svc = svci.(*service)//获取到注册服务时,注册的接口mtype = svc.method[methodName]if mtype == nil {err = errors.New("rpc: can't find method " + req.ServiceMethod)}return}

readRequest方法

func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *methodType, req *Request, argv, replyv reflect.Value, keepReading bool, err error) {service, mtype, req, keepReading, err = server.readRequestHeader(codec)//调用上面的readRequestHeader方法,进行解码,并返返回接口数据if err != nil {if !keepReading {return}// discard bodycodec.ReadRequestBody(nil)return}// Decode the argument value.argIsValue := false // if true, need to indirect before calling.//判断传擦是否为指针,如果为指针,需要使用Elem()方法,进行指向结构体if mtype.ArgType.Kind() == reflect.Ptr {argv = reflect.New(mtype.ArgType.Elem())} else {argv = reflect.New(mtype.ArgType)argIsValue = true}// argv guaranteed to be a pointer now.if err = codec.ReadRequestBody(argv.Interface()); err != nil {return}if argIsValue {argv = argv.Elem()}replyv = reflect.New(mtype.ReplyType.Elem())switch mtype.ReplyType.Elem().Kind() {case reflect.Map:replyv.Elem().Set(reflect.MakeMap(mtype.ReplyType.Elem()))case reflect.Slice:replyv.Elem().Set(reflect.MakeSlice(mtype.ReplyType.Elem(), 0, 0))}return}

call方法

func (s *service) call(server *Server, sending *sync.Mutex, wg *sync.WaitGroup, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {if wg != nil {defer wg.Done()}mtype.Lock()mtype.numCalls++mtype.Unlock()function := mtype.method.Func// Invoke the method, providing a new value for the reply.//调用call方法,并将参数转化为valueof型参数,returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})// The return value for the method is an error.//将返回的error进行读取,转化为interface{}型errInter := returnValues[0].Interface()errmsg := ""if errInter != nil {//将error进行断言errmsg = errInter.(error).Error()}server.sendResponse(sending, req, replyv.Interface(), codec, errmsg)server.freeRequest(req)}

注册的大概流程

接收请求的大概流程

感谢各位的阅读,以上就是“Golang原生rpc的原理是什么”的内容了,经过本文的学习后,相信大家对Golang原生rpc的原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯