SoFunction
Updated on 2025-03-05

Golang native rpc (RPC server source code interpretation)

How many conditions are required to create a rpc interface

  • The method type is outputable
  • The method itself can output
  • The method must have two parameters, it must be an output type or a built-in type
  • The second parameter of the method is the pointer type
  • The type returned by the method is error

Analysis of the principle of rpc service

Server side

  • Service registration
  • Handle network calls

Service registration Through reflection processing, the interface is stored in the map and the two methods of registering the service are called.

func Register (rcvr interface{}) error {}
func RegisterName (rcvr interface{} , name string) error {}
//Specify the registered name

Interpretation of the source code of the registration methodFirst of all, both the Register and RegisterName underlying code call the register method to register the service.Interpretation of register method

func (server *Server) register(rcvr interface{}, name string, useName bool) error {
	//Create a service instance	s := new(service)
	 = (rcvr)
	 = (rcvr)
	sname := ().Type().Name()
	//If the service name is empty, use the default service name	if useName {
		sname = name
	}
	if sname == "" {
		s := ": no service name for type " + ()
		(s)
		return (s)
	}
	//Judge whether the method name is exposed. If the method name is not exposed, it will cause the call to be unsuccessful, so return false	if !(sname) && !useName {
		s := ": type " + sname + " is not exported"
		(s)
		return (s)
	}
	 = sname

	// Install the methods
	//Call the suitableMethods function to return the interface, and determine whether the method meets the conditions as the rpc interface in the suitableMethods. If it meets, it will be added to services	 = suitableMethods(, true)

	if len() == 0 {
		str := ""

		// To help the user, see if a pointer receiver would work.
		//If the method is bound to the address of the structure, using () will not find the method, so you also need to search for the method bound to the address of the structure.		method := suitableMethods((), false)
		if len(method) != 0 {
			str = ": type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
		} else {
			str = ": type " + sname + " has no exported methods of suitable type"
		}
		(str)
		return (str)
	}
	//Discern whether the service interface has been registered.	if _, dup := (sname, s); dup {
		return ("rpc: service already defined: " + sname)
	}
	return nil
}

Interpretation of suitableMethod method

func suitableMethods(typ , reportErr bool) map[string]*methodType {
	//Create a slice of a method	methods := make(map[string]*methodType)
	for m := 0; m < (); m++ {
		method := (m)
		mtype := 
		mname := 
		// Method must be exported.
		if  != "" {
			continue
		}
		// Method needs three ins: receiver, *args, *reply.
		//If the passed parameters are not three, an error will be reported. Why are there three here?		//The structure instance is passed by default in the golang method body, so there are three parameters in total, request, *response, and structure instance.		if () != 3 {
			if reportErr {
				(": method %q has %d input parameters; needs exactly three\n", mname, ())
			}
			continue
		}
		// First arg need not be a pointer.
		argType := (1)
		if !isExportedOrBuiltinType(argType) {
			if reportErr {
				(": argument type of method %q is not exported: %q\n", mname, argType)
			}
			continue
		}
		// Second arg must be a pointer.
		//Defend whether the second parameter is a pointer, and if it is not a pointer, it returns false.		replyType := (2)
		if () !=  {
			if reportErr {
				(": reply type of method %q is not a pointer: %q\n", mname, replyType)
			}
			continue
		}
		// Reply type must be exported.
		if !isExportedOrBuiltinType(replyType) {
			if reportErr {
				(": reply type of method %q is not exported: %q\n", mname, replyType)
			}
			continue
		}
		// Method needs one out.
		//Return whether the result is a value and is error		if () != 1 {
			if reportErr {
				(": method %q has %d output parameters; needs exactly one\n", mname, ())
			}
			continue
		}
		// The return type of the method must be error.
		if returnType := (0); returnType != typeOfError {
			if reportErr {
				(": return type of method %q is %q, must be error\n", mname, returnType)
			}
			continue
		}
		//Add the interface to the service		methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
	}
	return methods
}

Requests will be continuously parsed after receiving the request. Two methods to parse the request readRequestHeader

func (server *Server) readRequestHeader(codec ServerCodec) (svc *service, mtype *methodType, req *Request, keepReading bool, err error) {
	// Grab the request header.
	//Receive the request and encode the request	req = ()
	err = (req)
	if err != nil {
		req = nil
		if err ==  || err ==  {
			return
		}
		err = ("rpc: server cannot decode request: " + ())
		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
//The encoded request is intervaled, so as long as the data on the left and right sides of . can be decoded	dot := (, ".")
	if dot < 0 {
		err = ("rpc: service/method request ill-formed: " + )
		return
	}
	serviceName := [:dot]
	methodName := [dot+1:]

	// Look up the request.
	svci, ok := (serviceName)
	if !ok {
		err = ("rpc: can't find service " + )
		return
	}
	svc = svci.(*service)
	//The registered interface when obtaining the registration service	mtype = [methodName]
	if mtype == nil {
		err = ("rpc: can't find method " + )
	}
	return
}

readRequest method

func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *methodType, req *Request, argv, replyv , keepReading bool, err error) {
	service, mtype, req, keepReading, err = (codec)
//Calculate the readRequestHeader method above to decode and return the interface data	if err != nil {
		if !keepReading {
			return
		}
		// discard body
		(nil)
		return
	}

	// Decode the argument value.
	argIsValue := false // if true, need to indirect before calling.
	//Discribing whether the transfer is a pointer. If it is a pointer, you need to use the Elem() method to point to the structure	if () ==  {
		argv = (())
	} else {
		argv = ()
		argIsValue = true
	}
	// argv guaranteed to be a pointer now.
	if err = (()); err != nil {
		return
	}
	if argIsValue {
		argv = ()
	}

	replyv = (())

	switch ().Kind() {
	case :
		().Set((()))
	case :
		().Set(((), 0, 0))
	}
	return
}

Call method

func (s *service) call(server *Server, sending *, wg *, mtype *methodType, req *Request, argv, replyv , codec ServerCodec) {
	if wg != nil {
		defer ()
	}
	()
	++
	()
	function := 
	// Invoke the method, providing a new value for the reply.
	//Calling the call method and converting the parameter into valueof parameter,	returnValues := ([]{, argv, replyv})
	// The return value for the method is an error.
	//Read the returned error and convert it into interface{} type	errInter := returnValues[0].Interface()
	errmsg := ""
	if errInter != nil {
	//Assume the error		errmsg = errInter.(error).Error()
	}
	(sending, req, (), codec, errmsg)
	(req)
}

The general process of registration

  • According to reflection, the interface is retrieved
  • Use method to determine whether the interface complies with the specifications as the rpc interface (there are two parameters, the second parameter is a pointer, and returns a parameter error)
  • If the specification does not comply with the specification, an error will be returned. If the specification is comply with the specification, it will be stored in the map and a call will be provided.

The approximate process of receiving requests

  • First, we continuously receive the data stream and decode it. After decoding, we need to use . as a separator to cut and read the data.
  • Search the read data in the registered map. If found, return the relevant service and other data.
  • Make a call

This is the article about Golang native rpc (RPC server source code interpretation). For more related Golang native rpc content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!