The definition of RPC is derived from Baidu Encyclopedia

  • Remote Procedure Call (RPC) – A protocol that requests services from a Remote computer program over the network without the need to understand the underlying network technology. The RPC protocol assumes the existence of certain transport protocols, such as TCP or UDP, to carry information data between communicating programs. In the OSI network communication model, RPC spans the transport layer and the application layer. RPC makes it easier to develop applications that include networked distributed multiprograms.

  • RPC adopts client/server mode. The requester is a client, and the service provider is a server. First, the client calling process sends an invocation message with process parameters to the server process, and then waits for the reply message. On the server side, the process stays asleep until the call information arrives. When a call message arrives, the server gets the process parameters, evaluates the results, sends a reply, and then waits for the next call message. Finally, the client calls the process to receive the reply, gets the process result, and then the call execution continues.

  • There are multiple RPC modes and implementations. Originally proposed by Sun. The IETF ONC Charter revises the Sun version to make the ONC RPC protocol the IETF standard. The most commonly used pattern and implementation today is the open software-based distributed Computing Environment (DCE).

  • Personal understanding: regardless of what the underlying network technology protocol, is a kind of network from the computer program to request services, vulgar, we write code, in a place, such as Android, it needs to be in a project, can call to other program code execution process. The Go language provides RPC support that makes it easier to develop networked, distributed, multiprogram applications

RPC flow chart

  • 1. Call the client handle. Execution transfer parameter
  • 2. Call the local system kernel to send network messages
  • 3. The message is sent to the remote host
  • 4. The server handle receives the message and obtains the parameter
  • 5. Perform the remote procedure
  • 6. The executed process returns the result to the server handle
  • 7. The server handle returns the result and calls the remote system kernel
  • 8. The message is sent back to the local host
  • 9. The customer handle receives messages from the kernel
  • 10. The customer receives the data returned by the handle

The Go language provides support for RPC:HTTP, TCP, and JSPNRPC, but inGoIn theRPCIs unique in its adoptionGoLang GobCode, only support Go language!

  • GoLang Gob: A data structure serialization encoding/decoding tool that comes with the GoLang package. Encoder is used for encoding, Decoder is used for decoding. A typical application scenario is remote Procedure calls (RPC).

HTTP RPC Demo

  • Server-side code
package main

import (
	"fmt"
	"net/rpc"
	"net/http"
	"errors"
)
func main() {
     rpcDemo()
}
type Arith int
func rpcDemo() {
	arith:=new(Arith)
	//arith=== 0xc04204e090
	fmt.Println("arith===",arith) rpc.register (arith) //HandleHTTP Registers the HTTP handler of RPC messages to the Debug server //DEFAUTUPCPATH and Debug handler on the Debug path. // You still need to call http.services (), usually in a GO statement. rpc.HandleHTTP() err:=http.ListenAndServe(": 1234",nil)
	iferr ! = nil { fmt.Println("err=====",err.Error())
	}
}
type Args struct {
	A, B int
}

type* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Error func (t *Arith) Multiply(args * args, reply *int) error { *reply = args.A * args.B fmt.Println("This method executes -- heh heh -- Multiply",reply)
	return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	fmt.Println("This method works -- hey hey -- Divide quo==",quo)
	return nil
}

Copy the code
  • Go RPC functions can be accessed remotely only if they meet the four conditions; otherwise, they are ignored
    • Functions must be uppercase (exported)
    • You must have two parameters of the exported type
    • The first argument is the argument accepted, the second argument is the argument returned to the client, and the second argument is the type of the pointer
    • The function also has a return valueerror
func (t *T) MethodName(argType T1, replyType *T2) error
Copy the code
  • The T, T1, and T2 types must be codec able by the Encoding /gob package.

  • Client code


package main

import (
	"log"
	"fmt"
	"os"
	"net/rpc"
	"strconv"
)

type ArgsTwo struct {
	A, B int
}

type QuotientTwo struct {
	Quo, Rem int
}

func main() {/ /, if what all don't input is below this value / / OS * * * * * * * * * * * * * * * * * [C: \ Users \ Windows 7 \ AppData \ Local \ Temp \ go - build669605574 \command- / / line - the arguments \ _obj \ exe \ GoRPCWeb exe 127.0.0.1] * * * * * * * * * * * * * * * * * * * * * * FMT. Println ("os*****************",os.Args,"* * * * * * * * * * * * * * * * * * * * * *")
	iflen(os.Args) ! = 4 {// todo the second address is our local address FMT.Println("I'm quitting, motherfucker. Start --------.", os.Args[0], "---------------server end")
		os.Exit(1)
	}else{
		fmt.Println("What is the length?"+strconv.Itoa( len(os.Args))+"That's the exact length --"} serverAddress := os.args [1] fmt.println (serverAddress := os.args [1] fst.println (serverAddress := os.args [1] fsT.println ("severAddress==",serverAddress) // //DelayHTTP Connects to the HTTP RPC server at the specified network address /// listens on the default HTTP RPC path. client, err := rpc.DialHTTP("tcp", serverAddress)
	iferr ! = nil { log.Fatal("There's an error here, DialHTTP.", err) } i1,_:=strconv.Atoi( os.Args[2]) i2,_:=strconv.Atoi( os.Args[3]) args := ArgsTwo{i1, I2} var reply int // call the named function, wait for it to complete, and return its error status. err = client.Call("Arith.Multiply", args, &reply)
	iferr ! = nil { log.Fatal(Call Multiply has an error., err)
	}
	fmt.Printf("Arith multiplication: %d*%d=%d\n"A, args.B, reply) var "QuotientTwo" // The call calls the named function, waits for it to complete, and returns its error status. err = client.Call("Arith.Divide", args, &quot)
	iferr ! = nil { log.Fatal("arith error:", err)
	}
	fmt.Printf(%d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)

}
Copy the code

E: \ new_code \ GoDemo \ web_server > go run GoRPCWeb8. Go 127.0.0.1:1234 20 3 OS * * * * * * * * * * * * * * * * * [C:\Users\win7\AppData\Local\Temp\go-build011170718\command- line - the arguments \ _obj \ exe \ GoRPCWeb8 exe 127.0.0.1:1234 20 3] * * * * * * * * * * * * * * * * * * * * * * what is the length 4 is the accurate length of oh - "severAddress = = 127.0.0.1:1234 Arith multiplication: 20*3=60 Arith division: 20/3=6 remainder 2Copy the code
  • Go run gorpcweb8.go 127.0.0.1:1234 20 3
    • Go run Indicates the run command
    • Gorpcweb8.go: This is the name of the file that needs to be started in the specified directorycmd
    • 127.0.0.1:1234: INDICATES the IP address and port number
    • 20 3 This is the value passed in by the client: one divisor, one dividend, passed in to the server for multiplication multiplication:20 * 3 = 60Sum division to integer:20/3 = 6remainder2How to do, the client does not care, to the server to complete
  • os.Args[0]=[C:\Users\win7\AppData\Local\Temp\go-build011170718\command- line-arguments\_obj\exe\ gorpcweb8.exe 127.0.0.1:1234 20 3]
	iflen(os.Args) ! = 4 {// todo the second address is our local address FMT.Println("I'm quitting, motherfucker. Start --------.", os.Args[0], "---------------server end")
		os.Exit(1)
	}else{
		fmt.Println("What is the length?"+strconv.Itoa( len(os.Args))+"That's the exact length --")}Copy the code

TCP RPC Demo

  • RPC based on TCP protocol, server code
package main

import (
	"fmt"
	"net/rpc"
	"net"
	"os"
	"errors"
)

func init() {
	fmt.Println("Based on the TCP protocol implementation of RPC, the server side code is as follows")}type Me struct {
	A,B int
}
type You struct {
	CC,D int
}
typeNum int /* Go RPC functions can be remotely accessed only if they meet the following conditions: 1) the function must be exported (first letter uppercase) 2) the function must have two arguments of the exported type 3) The first argument is accepted, the second argument is returned to the client, The function must also have A return value error */ func (n *Num) M(args *Me,reply *int) error {*reply=args.A * args.Breturn nil
}


func (n *Num) F(args * Me,u *You ) error  {
	if  args.B==0{
		return errors.New("The input cannot be a dividend of 0.")
	}
	u.D=args.A/args.B
	u.CC=args.A % args.B
	return nil
}



func main() {// The built-in function new essentially does the same thing as the function of the same name in other languages: new(T) allocates a zero-filled memory space of type T and returns its address, which is a value of type *T. In Go terms, it returns a pointer to the zero value of the newly allocated type T. It is important to note that //new returns a pointer. Num :=new(num) rpc.register (num) //ResolveTCPAddr Returns the address of the TCP endpoint. // The network must be TCP network name. tcpAddr,err:=net.ResolveTCPAddr("tcp".": 1234")

	iferr ! = nil { fmt.Println("Wrong.")
		os.Exit(1)
	}
    listener,err:=net.ListenTCP("tcp",tcpAddr)
	for{// todo needs to control the connection itself. When a client is connected, we need to pass the connection to RPC to handle the conn,err:= listener.accept ()iferr ! = nil {continue
		}
		rpc.ServeConn(conn)
	}
}

Copy the code
  • Based on TCP client code
package main

import (
	"fmt"
	"os"
	"net/rpc"
	"log"
	"strconv"
)

func main() {
	fmt.Println("The corresponding example for where the rest of the client will call is gotcprpc9.go")

	if len(os.Args)==4{
		fmt.Println("Length has to be equal to 4, because you must be typing in an IP address IP =",os.Args[1],Os. Args[2]=",os.Args[2],Args[3]=", os.args [3]) // os.exit (1)} // Get the IP address service:= os.args [1] // Connect the dial-up to the RPC server at the specified network address. client,err:=rpc.Dial("tcp",service)
	iferr! =nil { log.Fatal("I made a mistake connecting Dial. I'm quitting.",err)
	}
	num1:=os.Args[2]
	i1,error1:=strconv.Atoi(num1)
	iferror1! =nil { fmt.Println("I don't know if I made a mistake. Please look at error:",error1)
		os.Exit(1)
	}
	num2:=os.Args[3]
	i2,error2:=strconv.Atoi(num2)
	iferror2! =nil { fmt.Println("I don't know if I made a mistake. Please look at error:",error2)
		os.Exit(1)
	}
	aa:=AAA{i1,i2}
	var reply  int
	err1:=client.Call("Num.M",aa,&reply)

	iferr1 ! = nil{ log.Fatal("I'm going to quit because I made an error on the Call.",err1)
	}
	fmt.Println("I run the normal result as follows.")
	fmt.Printf("Num : %d*%d=%d\n", aa.a, aa.b,reply) var bb BDemo // call the named function, wait for it to complete, and return its error status. err= client.Call("Num.F",aa,&bb)
	iferr! =nil { log.Fatal("I'm having an allergic reaction to this method hahaha err=====",err)
	}
	fmt.Printf("Num: %d/%d=%d \n"A, aa.b, bb.dd, bb.cc)} // Define two classes, the one that needs to be operated ontypeAAA struct {A,B int} // struct {A,B int}type mismatch: no fields matched compiling decoder forDDDD // todo why the second argument is an error as long as two connected DDDD:type mismatch: no fields matched compiling decoder for
type BDemo struct {
	DD, CC int
}

Copy the code
  • Running result diagram
E:\new_code\GoDemo\web_server>go run gotcprpcweb10.go 127.0.0.1:1234 20 1 Where the other end of the client goes to call the corresponding example is gotcprpc9.go Args[3]= 1 Num: 20*1=20 Num: 20*1=20 Num: 20*1=20 Num: 20/1=0 remainder 0 E:\new_code\GoDemo\web_server>go run gotcprpcweb10.go 127.0.0.1:1234 20 2 The corresponding example where the other end of the client goes to call is gotcprpc9.go Num: 20*2=40 Num: 20*2=40 Num: 20*2=40 Num: 20*2=40 Num: 20*2=40 20/2=0 remainder 0 E:\new_code\GoDemo\web_server>go run gotcprpcweb10.go 127.0.0.1:1234 20 3 The corresponding example where the other end of the client goes to call is gotcprpc9.go Num: 20*3=60 Num: 20*3=60 Num: 20*3=60 Num: 20*3=60 Num: 20*3=60 Num: 20/3 = 0 remainder 2Copy the code
  • In the definitionBDemoIf definedDD, CC intUnlike the server, it will report an errorreading body gob: type mismatch: no fields matched compiling decoder forIn fact, found a variety of cases, will also appear this error, but at present do not know why so, later, such as source code in-depth, come back to see this problem todo2018/07/19
  • This kind ofTCPThe way and the aboveHTTPThe difference is
    • HTTP: The specified network address is connected to the HTTP RPC server
DelayHTTP connects to the HTTP RPC server at the specified network address /// listens on the default HTTP RPC path. client, err := rpc.DialHTTP("tcp", serverAddress)
Copy the code
  • TCP: The specified network address is connected to the HTTP RPC server
    client,err:=rpc.Dial("tcp",service)
Copy the code

JSON RPC

  • JSON RPC is the data encoding using JSON instead of GOB encoding, otherwise identical to RPC concepts described above.

  • The server code is as follows

package main

import (
	"fmt"
	"net/rpc"
	"net"
	"net/rpc/jsonrpc") // Use the JSON-RPC standard package func provided by Goinit() {
	fmt.Println("JSON RPC uses JSON instead of GOB encoding, which is exactly the same concept as RPC,")}type Work struct {
	Who,DoWhat string
}

type DemoM string

func (m *DemoM) DoWork(w *Work,whoT *string) error  {
    *whoT="Who is it?"+w.Who+"What are you doing?"+w.DoWhat
	return nil
}

func main() {
    str:=new(DemoM)
    rpc.Register(str)

    tcpAddr,err:=net.ResolveTCPAddr("tcp".": 8080")
	iferr! =nil{ fmt.Println("ResolveTCPAddr err=",err)
	}

    listener,err:=net.ListenTCP("tcp",tcpAddr)
	iferr! =nil { fmt.Println("Err =",err)
	}

	for  {
		 conn,err:= listener.Accept()
		iferr! =nil {continue
		}
		jsonrpc.ServeConn(conn)

	}

}

Copy the code
  • Client code
package main

import (
	"fmt"
	"os"
	"net/rpc/jsonrpc"
	"log"
)

func main() {
	fmt.Println("This is the client, it starts, it starts from the command line.")

	fmt.Println("The corresponding example for where the rest of the client will call is gotcprpc9.go")

	if len(os.Args)==4{
		fmt.Println("Length has to be equal to 4, because you must be typing in an IP address IP =",os.Args[1],Os. Args[2]=",os.Args[2],Args[3]=",os.Args[3])
		//os.Exit(1)
	}

	 service:=os.Args[1]
	 client,err:=jsonrpc.Dial("tcp",service)
	iferr ! = nil { log.Fatal("There's something wrong with this Dial and the error message is err=",err)
	}
	send:=Send{os.Args[2],os.Args[3]}
	var  resive  string
	err1:=client.Call("DemoM.DoWork",send,&resive)
	iferr1! =nil { fmt.Println("shiming call error ")
		fmt.Println("There was an error when I called err=",err1)
	}
	fmt.Println("Got the message."} // the value of Who and DoWhat must be the same, or else the value will not be receivedtype Send struct {
	Who, DoWhat string
}





Copy the code
  • The result of this run is as follows
E:\new_code\GoDemo\web_server>go run gojsonrpcweb11.go 127.0.0.1:8080 shiming g ongzuo GoTCPRPC9. Go length must be equal to 4 because, Args[2]= shiming and os.Args[3]= gongzuo Go run gojsonrpcweb11.go 127.0.0.1:8080 shiming q iaodaima this is the client, GoTCPRPC9. Go has to be 4 in length because, Args[2]= shiming and os.Args[3]= qiaodaima Shiming, what are you doing -- QiaodaimaCopy the code
  • os.ArgsIt’s an arrayvar Args []stringThe server does something about it and then returns it to the client
  • GoAlready providedRPCGood support throughHTTP TCP JSONRPCIt can be very convenient to develop distributedWebApplication, but I can not yet, in learning. It is a pity thatGoDoes not provideSOAP RPCSupport ~ ~ ~