Abstract: In general, HTTP is a waste of resources per request. Although HTTP also walks on TOP of TCP, HTTP requests add much of their own information and thus consume bandwidth resources. So some companies use RPC as the communication protocol for internal applications. The original

If you are also interested in Go, you can follow my official account: GoGuider

RPC

Remote Procedure Call (RPC) is an application communication protocol that requests services from Remote computer programs over a network without understanding the details of the underlying network. The RPC protocol is built on top of TCP or UDP, or HTTP.

In Go, the net/ RPC package provided by the standard library realizes the relevant details required by the RPC protocol, and developers can easily use this package to write RPC server and client programs.

From the figure above, RPC itself is a client-server model.

Here is an example code to understand the RPC call process

server.go

package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"os"
	"time"
)

type Args struct {
	A, B int
}

type Math int

// Compute the product
func (t *Math) Multiply(args *Args, reply *int) error {
	time.Sleep(time.Second * 3) // Sleep for 1 second. Synchronous calls wait and asynchronous calls proceed first
	*reply = args.A * args.B
	fmt.Println("Multiply")
	return nil
}

/ / and calculation
func (t *Math) Sum(args *Args, reply *int) error {
	time.Sleep(time.Second * 3)
	*reply = args.A + args.B
	fmt.Println("Sum")
	return nil
}

func main(a) {
	// Create an object
	math := new(Math)
	// The RPC service registers a Math object exposed method for clients to call
	rpc.Register(math)
	The HTTP protocol is used as the carrier of RPC calls. RPC.ServeConn can also be used to handle individual connection requests
	rpc.HandleHTTP()
	l, e := net.Listen("tcp".": 1234")
	ife ! =nil {
		log.Fatal("listen error", e)
	}
	go http.Serve(l, nil)
	os.Stdin.Read(make([]byte.1))}Copy the code

client.go

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"time"
)

type Args struct {
	A, B int
}

func main() {// Establish a connection with the RPC server before calling the method provided by the RPC server. Client, err := rpc.dialhttp ("tcp"."127.0.0.1:1234")
	iferr ! = nil { log.Fatal("dialHttp error", err)
		return} args := &args {7, 8} var reply int Err = client.call (err = client.call ("Math.Multiply", args, &reply) // This will block for three secondsiferr ! = nil { log.Fatal("call Math.Multiply error", err)
	}
	fmt.Printf("Multiply:%d*%d=%d\n"Var sum int divCall := client.go ("Math.Sum", args, &sum, nil) // Use the SELECT model to listen for data on the channel, otherwise execute the subsequent programfor {
		select {
		case <-divCall.Done:
			fmt.Printf("%d+%d is %d, exit execution!", args.A, args.B, sum)
			return
		default:
			fmt.Println("Keep waiting....")
			time.Sleep(time.Second * 1)
		}
	}
}
Copy the code

Run the command

go run server.go

go run client.go

Copy the code

The results

Multiply:7*8=56 Continue to wait.... Continue to wait.... Continue to wait.... 7+8=15Copy the code

Call procedure resolution

The server side

  • The RPC service registers a Math object exposing method for clients to invoke
  • The HTTP protocol is used as the carrier of RPC calls to process requests

The client side

  • Before invoking the methods provided by the RPC server, establish a connection with the RPC server
  • Call remote methods using the Call method

extension

If you are careful, you will notice that there are client.Call and client.

Check the source code to see the client.Call base is called client.Go

// Go invokes thefunction asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
	call := new(Call)
	call.ServiceMethod = serviceMethod
	call.Args = args
	call.Reply = reply
	if done == nil {
		done = make(chan *Call, 10) // buffered.
	} else {
		// If caller passes done! = nil, it must arrange that //done has enough buffer for the number of simultaneous
		// RPCs that will be using that channel. If the channel
		// is totally unbuffered, it's best not to run at all. if cap(done) == 0 { log.Panic("rpc: done channel is unbuffered") } } call.Done = done client.send(call) return call } // Call invokes the named function, waits for it to complete, and returns its error status. func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error { call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done return call.Error }Copy the code

Refer to the article

  • Official document of gRPC
  • GRPC Chinese document