Several ways golang implements RPC
https://studygolang.com/articles/14336
What is RPC
Remote Procedure Call (RPC) is a computer communication protocol.This protocol allows programs running on one computer to call subprograms from another without additional programming for this interaction by the programmer.If the software involved is object-oriented, remote procedure calls can also be referred to as remote calls or remote method calls. Wikipedia: Remote Procedure Calls
An easy-to-understand language description is that RPC allows computer program methods to be invoked across machines and languages.For example, I wrote a method getUserInfo to get user information in go language and deployed the Go program on the Ali cloud server. Now I have a php project deployed on Tencent cloud. I need to call golang's getUserInfo method to get user information. The process of php calling go method across machines is RPC call.
How to implement RPC in golang
Implementing RPC in golang is very simple, supported by encapsulated official libraries and some third-party libraries.Go RPC can use tcp or http to transfer data, and can use many types of codec methods for the data to be transferred.The official net/rpc Library of golang uses encoding/gob for encoding and decoding, supports tcp or http data transmission. Because other languages do not support gob encoding and decoding, the RPC method implemented by net/rpc library cannot be called across languages.
The golang official also provides the net/rpc/jsonrpc library to implement the RPC method, which uses JSON for data encoding and decoding, thus supporting cross-language calls.However, the current jsonrpc library is based on the tcp protocol and does not support data transmission using http at this time.
In addition to the official RPC library provided by golang, there are many third-party libraries to support the implementation of RPC in golang. Most third-party RPC libraries are implemented by using protobuf for data encoding and decoding, automatically generating RPC method definitions and service registration codes based on the protobuf declaration file, which makes it very convenient to perform RPC services in golangCalled.
net/rpc Library
The following example demonstrates how to implement RPC using golang's official net/rpc library, using HTTP as the carrier of RPC, and listening for client connection requests through the net/http package.
$GOPATH/src/test/rpc/rpc_server.go
package main import ( "errors" "fmt" "log" "net" "net/http" "net/rpc" "os" ) // Arithmetic operation structure type Arith struct { } // Arithmetic operation request structure type ArithRequest struct { A int B int } // Arithmetic operation response structure type ArithResponse struct { Pro int // product Quo int // merchant Rem int // Remainder } // Multiplication func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error { res.Pro = req.A * req.B return nil } // Division operation method func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error { if req.B == 0 { return errors.New("divide by zero") } res.Quo = req.A / req.B res.Rem = req.A % req.B return nil } func main() { rpc.Register(new(Arith)) // Register rpc service rpc.HandleHTTP() // Using http protocol as rpc carrier lis, err := net.Listen("tcp", "127.0.0.1:8095") if err != nil { log.Fatalln("fatal error: ", err) } fmt.Fprintf(os.Stdout, "%s", "start connection") http.Serve(lis, nil) }
When the above server-side program runs, it will listen on the local port 8095. We can implement a client program, connect the server-side and implement RPC method calls.
$GOPATH/src/test/rpc/rpc_client.go
package main import ( "fmt" "log" "net/rpc" ) // Arithmetic operation request structure type ArithRequest struct { A int B int } // Arithmetic operation response structure type ArithResponse struct { Pro int // product Quo int // merchant Rem int // Remainder } func main() { conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095") if err != nil { log.Fatalln("dailing error: ", err) } req := ArithRequest{9, 2} var res ArithResponse err = conn.Call("Arith.Multiply", req, &res) // Multiplication if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro) err = conn.Call("Arith.Divide", req, &res) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem) }
net/rpc/jsonrpc Library
The above example demonstrates the process of implementing RPC using net/rpc, but it is not possible to call the RPC method implemented in the above example in another language.So the next example demonstrates using the net/rpc/jsonrpc library to implement the RPC method, which supports cross-language calls.
$GOPATH/src/test/rpc/jsonrpc_server.go
package main import ( "errors" "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" "os" ) // Arithmetic operation structure type Arith struct { } // Arithmetic operation request structure type ArithRequest struct { A int B int } // Arithmetic operation response structure type ArithResponse struct { Pro int // product Quo int // merchant Rem int // Remainder } // Multiplication func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error { res.Pro = req.A * req.B return nil } // Division operation method func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error { if req.B == 0 { return errors.New("divide by zero") } res.Quo = req.A / req.B res.Rem = req.A % req.B return nil } func main() { rpc.Register(new(Arith)) // Register rpc service lis, err := net.Listen("tcp", "127.0.0.1:8096") if err != nil { log.Fatalln("fatal error: ", err) } fmt.Fprintf(os.Stdout, "%s", "start connection") for { conn, err := lis.Accept() // Receive client connection requests if err != nil { continue } go func(conn net.Conn) { // Concurrent processing of client requests fmt.Fprintf(os.Stdout, "%s", "new client in coming\n") jsonrpc.ServeConn(conn) }(conn) } }
When the above server-side program starts, it will listen to the local port 8096 and process the client's tcp connection requests.We can use golang to implement a client program that connects the above servers and makes RPC calls.
$GOPATH/src/test/rpc/jsonrpc_client.go
package main import ( "fmt" "log" "net/rpc/jsonrpc" ) // Arithmetic operation request structure type ArithRequest struct { A int B int } // Arithmetic operation response structure type ArithResponse struct { Pro int // product Quo int // merchant Rem int // Remainder } func main() { conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096") if err != nil { log.Fatalln("dailing error: ", err) } req := ArithRequest{9, 2} var res ArithResponse err = conn.Call("Arith.Multiply", req, &res) // Multiplication if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro) err = conn.Call("Arith.Divide", req, &res) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem) }
protorpc Library
In order to achieve cross-language call, when implementing RPC method in golang, we should choose a cross-language data encoding and decoding method, such as JSON. The above jsonrpc can meet this requirement, but it also has some drawbacks, such as not supporting http transmission and low data encoding and decoding performance.Thus, some third-party RPC libraries choose to use protobuf for data encoding and decoding, and provide some automatic generation of service registration codes.The following example uses protobuf to define RPC methods and request response parameters, and third-party protorpc libraries to generate RPC service registration codes.
First, you need to install protobuf and the protoc executable commands, which you can refer to in this article: protobuf Quick Upper Finger Guide
Then, we write a proto file that defines the RPC method to be implemented and its associated parameters.
$GOPATH/src/test/rpc/pb/arith.proto
syntax = "proto3"; package pb; // Arithmetic operation request structure message ArithRequest { int32 a = 1; int32 b = 2; } // Arithmetic operation response structure message ArithResponse { int32 pro = 1; // product int32 quo = 2; // merchant int32 rem = 3; // Remainder } // rpc method service ArithService { rpc multiply (ArithRequest) returns (ArithResponse); // Multiplication rpc divide (ArithRequest) returns (ArithResponse); // Division operation method }
Next we need to generate the RPC service code from the arith.proto file defined above.
To install the protorpc library first: go get github.com/chai2010/protorpc
Then use the protoc tool to generate the code: protoc --go_out=plugin=protorpc=. arith.proto
After executing the protoc command, an arith.pb.go file containing the code for the RPC method definition and service registration was generated in a directory equivalent to the arith.proto file.
We implement an rpc server based on the generated arith.pb.go code
$GOPATH/src/test/rpc/protorpc_server.go
package main import ( "errors" "test/rpc/pb" ) // Arithmetic operation structure type Arith struct { } // Multiplication func (this *Arith) Multiply(req *pb.ArithRequest, res *pb.ArithResponse) error { res.Pro = req.GetA() * req.GetB() return nil } // Division operation method func (this *Arith) Divide(req *pb.ArithRequest, res *pb.ArithResponse) error { if req.GetB() == 0 { return errors.New("divide by zero") } res.Quo = req.GetA() / req.GetB() res.Rem = req.GetA() % req.GetB() return nil } func main() { pb.ListenAndServeArithService("tcp", "127.0.0.1:8097", new(Arith)) }
Running the above program will listen to the local port 8097 and receive tcp connections from clients.
Implement another client based on ariti.pb.go.
$GOPATH/src/test/protorpc_client.go
package main import ( "fmt" "log" "test/rpc/pb" ) func main() { conn, err := pb.DialArithService("tcp", "127.0.0.1:8097") if err != nil { log.Fatalln("dailing error: ", err) } defer conn.Close() req := &pb.ArithRequest{9, 2} res, err := conn.Multiply(req) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d * %d = %d\n", req.GetA(), req.GetB(), res.GetPro()) res, err = conn.Divide(req) if err != nil { log.Fatalln("arith error ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem) }
How to call golang's RPC method across languages
The above three examples, we use net/rpc, net/rpc/jsonrpc, protorpc to implement the RPC server in golang, and give the corresponding golang client RPC call examples. Because JSON and protobuf support multiple languages, we can use jsonrpc and protorpc to implement the RPC method in other languages.Line call.The following is a php client program that invokes the server-side RPC method implemented by jsonrpc through a socket connection.
$PHPROOT/jsonrpc.php
<?php class JsonRPC { private $conn; function __construct($host, $port) { $this->conn = fsockopen($host, $port, $errno, $errstr, 3); if (!$this->conn) { return false; } } public function Call($method, $params) { if (!$this->conn) { return false; } $err = fwrite($this->conn, json_encode(array( 'method' => $method, 'params' => array($params), 'id' => 0, ))."\n"); if ($err === false) { return false; } stream_set_timeout($this->conn, 0, 3000); $line = fgets($this->conn); if ($line === false) { return NULL; } return json_decode($line,true); } } $client = new JsonRPC("127.0.0.1", 8096); $args = array('A'=>9, 'B'=>2); $r = $client->Call("Arith.Multiply", $args); printf("%d * %d = %d\n", $args['A'], $args['B'], $r['result']['Pro']); $r = $client->Call("Arith.Divide", array('A'=>9, 'B'=>2)); printf("%d / %d, Quo is %d, Rem is %d\n", $args['A'], $args['B'], $r['result']['Quo'], $r['result']['Rem']);
Other RPC Libraries
In addition to the three ways mentioned above to implement RPC in golang, there are other RPC libraries that provide similar functionality. The well-known open source grpc is google, but the initial installation of grpc is more cumbersome and will not be further described here. Those who are interested can learn about it themselves.
Reference material
This article is from: Short Book
Thank you for the author: Douban milk tea
View the original text: Several ways golang implements RPC