乘风原创程序

  • Golang原生rpc(rpc服务端源码解读)
  • 2022/4/7 16:08:16
  • 创建rpc接口,需要几个条件

    • 方法的类型是可输出的
    • 方法的本身也是可输出的
    • 方法必须有两个参数,必须是输出类型或者是内建类型
    • 方法的第二个参数是指针类型
    • 方法返回的类型为error

    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)
    	}
    	//判断方法名是否暴漏的,如果方法名不是暴露的,则会导致调用不成功,所以返回false
    	if !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.type
    		mname := 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.
    		//返回结果是否为一个值,且为error
    		if 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
    		}
    		//将接口加入service
    		methods[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 = nil
    		if 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 body
    		codec.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)
    }

    注册的大概流程

    • 根据反射,进行接口的获取
    • 使用方法判断接口是否符合作为rpc接口的规范(有两个参数,第二个参数为指针,返回一个参数error)
    • 如果不符合规范,将返回error,符合规范,将存入map,进行提供调用

    接收请求的大概流程

    • 首先,不断的接收数据流,并进行解码,解码之后为data.data,所以我们需要使用 . 作为分隔符,进行数据的截切和读取
    • 将读取的数据在注册的map中进行查找,如果查找到,返回相关的service和其他数据
    • 进行调用

    到此这篇关于golang原生rpc(rpc服务端源码解读)的文章就介绍到这了,更多相关golang原生rpc内容请搜索本教程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持本教程网!