What is RPC service
Remote Procedure Call (RPC) is A Call method between different nodes in A distributed system. It can be understood as: Server A invokes functions/methods provided by the application on server B. RPC is initiated by the client and communicates by calling methods on the server. The server then returns the result to the client.
There are two core points of RPC: communication protocol and serialization. Serialization and deserialization are the encoding and decoding methods of transmitted data. Common encoding and decoding methods include JSON and Protobuf.
The flow of RPC call:
- The client invokes the client handle and passes parameters to the client handle
- The client handle wraps and encodes the parameters
- The client local system sends information to the server
- The server system sends the information to the server handle
- Server handle parsing information (decoding)
- The server handle invokes the real server program
- The server processes it and returns the result to the client in the same way
Network communication is generally through Socket communication.
Go Language SIMPLE introduction to RPC
In the Go SDK, the NET/RPC package is built in to implement RPC. Net/RPC packages provide the ability to access server-side object methods over the network.
We show the RPC call through an addition run, server example:
server/math_server.go
package server
type MathService struct{}type Args struct {
A, B int
}
func (m *MathService) Add(args Args, reply *int) error {
*reply = args.A + args.B
return nil
}
Copy the code
In the code above:
- Defines a MathService that represents a remote service object;
- Args structure represents parameters;
- The Add method implements the addition function and returns the result via the replay pointer variable.
With this service object, you can register it in the list of exposed services for use by other clients. To register an RPC service object, use the RegisterName method:
server_main.go
package main
import (
"log"
"net"
"net/rpc"
"rpctest/server"
)
func main(a) {
rpc.RegisterName("MathService".new(server.MathService))
l, err := net.Listen("tcp".": 8088") // Don't forget to write
iferr ! =nil {
log.Fatal("listen error", err)
}
rpc.Accept(l)
}
Copy the code
In the code above:
- A service object is registered with the RegisterName function, which takes two arguments:
- Service name, used by the client when invoked (MathService)
- The service object, which is the MathService structure
- Through the net.Listen function to establish a TCP link, in port 8088 for monitoring;
- Finally, MathService, the RPC service, is provided on the TCP connection through the rpc.Accept function. This way, the client can see the MathService service and its Add method.
Net/RPC provides the RPC framework. In order to register an object as an RPC service that can be remotely accessed by clients, methods on the object (type) must meet the following conditions:
- The type of method is exportable (public);
- Methods themselves are also derivable;
- The method must have two parameters of either exportable or built-in type;
- Method must return an error type.
The summary is:
func (t *T) MethodName(argType T1, replyType *T2) error
Copy the code
T1 and T2 are serialized with Encoding/GOB.
- The first argument, argType, is provided by the caller (client);
- The second argument, replyType, is the result returned to the caller and must be of pointer type.
We are done with the RPC service and continue to complete the client call:
client_main.go
package main
import (
"fmt"
"log"
"net/rpc"
"rpctest/server"
)
func main(a) {
client, err := rpc.Dial("tcp"."localhost:8088")
iferr ! =nil {
log.Fatal("dialing")
}
args := server.Args{A: 1, B: 2}
var reply int
err = client.Call("MathService.Add", args, &reply)
iferr ! =nil {
log.Fatal("MathService.Add error", err)
}
fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)
}
Copy the code
In the code above:
- Use the rpc.Dial function to establish a TCP connection. Note that the IP address and port must be the same as those provided by the RPC service.
- Prepare the parameters required for the remote method, in this case ARgs and reply;
- Call the remote RPC service through the Call method;
- The Call method takes three arguments, which do the following:
- The name of the remote method called, in this case mathService.add, the part before the dot is the name of the registered service, and the part after the dot is the method of the service;
- A parameter provided by the client to call a remote method, in this case args;
- In order to receive the result returned by the remote method, it must be a pointer, in this case &replay.
Server side and client side we have completed, overall directory structure:
Start running:
- First run the server program to provide RPC service:
go run server_main.go
- Then run the client program and call RPC:
go run client_main.go
Running results:
MathService.Add: 1+2=3
Copy the code
If the preceding result is displayed, the RPC call is successful. ✿ Angry, (° °) Blue ✿
Http-based RPC
RPC can be called not only through TCP protocol, but also through HTTP protocol. It can also be called through the built-in NET/RPC package. We change the door-to-door code to call HTTP protocol, and the server code:
server_main.go
func main(a) {
rpc.RegisterName("MathService".new(server.MathService))
rpc.HandleHTTP()/ / added
l, err := net.Listen("tcp".": 8088")
iferr ! =nil {
log.Fatal("listen error:", err)
}
http.Serve(l, nil)// Switch to an HTTP service
}
Copy the code
Client part code modification:
client_main.go
func main(a) {
client, err := rpc.DialHTTP("tcp"."localhost:8088")
// Omit other unmodified code here
}
Copy the code
After the modification is complete, we run the server and client separately, and the result is the same as the TCP connection.
Debug URL
Net/RPC package provides the HTTP protocol of RPC and a debug URL, after running the server code, visit http://localhost:8088/debug/rpc in the browser enter, to be able to see the server registered RPC service, and the method of each service, as shown in figure:
You can see the registered RPC service, the method’s signature, and the number of times it has been called.
JSON RPC cross-platform communication
The RPC service implemented above is based on GOB code, which is difficult to call across languages, but the implementers and callers of RPC services may be different programming languages, so the RPC service we implemented should support multi-language call.
TCP JSON RPC
To implement RPC services across languages, the core is to choose a common encoding, such as the common JSON. In the Go language, a JSON RPC service can be implemented using only the NET/RPC/JSONRPC package.
I transformed the above example into a JSON-enabled RPC service with the following server-side code:
server_main.go
func main(a) {
rpc.RegisterName("MathService".new(server.MathService))
l, err := net.Listen("tcp".": 8088")
iferr ! =nil {
log.Fatal("listen error:", err)
}
for {
conn, err := l.Accept()
iferr ! =nil {
log.Println("jsonrpc.Serve: accept:", err.Error())
return
}
//json rpc
go jsonrpc.ServeConn(conn)
}
}
Copy the code
In the above code, compared with the RPC service encoded by GOB, the JSON RPC service delivers the link to the function jsonRpc.ServeConn for processing, achieving the purpose of RPC call based on JSON.
The JSON RPC client code is modified as follows:
client_main.go
func main(a) {
client, err := jsonrpc.Dial("tcp"."localhost:8088")
// Omit other unmodified code
}
Copy the code
Simply replace the Dial method for establishing the link with the jsonRPC package.
HTTP JSON RPC
The built-in JSONRPC of Go language does not realize the transmission based on HTTP. Here we refer to the IMPLEMENTATION of HTTP RPC encoded by GOB to realize THE JSON RPC service based on HTTP.
server_main.go
func main(a) {
rpc.RegisterName("MathService".new(server.MathService))
// Register a PATH to provide HTTP based JSON RPC services
http.HandleFunc(rpc.DefaultRPCPath, func(rw http.ResponseWriter, r *http.Request) {
conn, _, err := rw.(http.Hijacker).Hijack()
iferr ! =nil {
log.Print("rpc hijacking ", r.RemoteAddr, ":", err.Error())
return
}
var connected = "200 Connected to JSON RPC"
io.WriteString(conn, "HTTP / 1.0"+connected+"\n\n")
jsonrpc.ServeConn(conn)
})
l, err := net.Listen("tcp".": 8088")
iferr ! =nil {
log.Fatal("listen error:", err)
}
http.Serve(l, nil)// Switch to an HTTP service
}
Copy the code
The above code implements the core of the HTTP protocol, using HTTP.handlefunc to register a path to provide JSON RPC services based on HTTP externally. In the implementation of the HTTP service, the Hijack method is used to Hijack the link and then forward it to JsonRPC for processing, thus realizing the JSON RPC service based on the HTTP protocol.
Client call code:
func main(a) {
client, err := DialHTTP("tcp"."localhost:8088")
iferr ! =nil {
log.Fatal("dialing:", err)
}
args := server.Args{A:1,B:2}
var reply int
err = client.Call("MathService.Add", args, &reply)
iferr ! =nil {
log.Fatal("MathService.Add error:", err)
}
fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)
}
// DialHTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialHTTP(network, address string) (*rpc.Client, error) {
return DialHTTPPath(network, address, rpc.DefaultRPCPath)
}
// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*rpc.Client, error) {
var err error
conn, err := net.Dial(network, address)
iferr ! =nil {
return nil, err
}
io.WriteString(conn, "GET "+path+"HTTP / 1.0 \ n \ n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "GET"})
connected := "200 Connected to JSON RPC"
if err == nil && resp.Status == connected {
return jsonrpc.NewClient(conn), nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + "" + address,
Addr: nil,
Err: err,
}
}
Copy the code
The core of the above code is to call the remote HTTP JSON RPC service by sending an HTTP request through the established TCP link, using the HTTP GET method. Run the server and client separately, and you’ll see the correct HTTP JSON RPC call results.
The RPC framework that comes with Go language is used above, but it is rarely used in actual development. The gRPC framework of Google is more commonly used, which is serialized through Protobuf, binary transmission based on HTTP/2 protocol, and supports many programming languages. Efficiency is also relatively high. It is easy to get started by studying RPC and then gRPC.