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