什么是RPC
远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。
用通俗易懂的语言描述就是:RPC允许跨机器、跨语言调用计算机程序方法。
golang中如何实现RPC
在golang中实现RPC非常简单,有封装好的官方库和一些第三方库提供支持。Go RPC可以利用tcp或http来传递数据,可以对要传递的数据使用多种类型的编解码方式。golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp或http数据传输方式,由于其他语言不支持gob编解码方式,所以使用net/rpc库实现的RPC方法没办法进行跨语言调用。
golang官方还提供了net/rpc/jsonrpc库实现RPC方法,JSON RPC采用JSON进行数据编解码,因而支持跨语言调用。但目前的jsonrpc库是基于tcp协议实现的,暂时不支持使用http进行数据传输。
除了golang官方提供的rpc库,还有许多第三方库为在golang中实现RPC提供支持,大部分第三方rpc库的实现都是使用protobuf进行数据编解码,根据protobuf声明文件自动生成rpc方法定义与服务注册代码,在golang中可以很方便的进行rpc服务调用。
net/rpc库
使用net/rpc库实现乘除法的rpc
rpc服务端:
$GOPATH/src/leitty/rpc/netrpc/server/rpc_server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package main
import ( "fmt" "errors" "net/rpc" "net/http" "net" "os" )
type Arith struct {}
type ArithRequest struct { A int B int }
type ArithResponse struct { Pro int Quo int Rem int }
func (this *Arith) Multiply(req ArithRequest,res *ArithResponse) error { res.Pro = req.A * req.B return nil }
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)) rpc.HandleHTTP()
lis, err := net.Listen("tcp","127.0.0.1:8095") if err != nil { panic(err) }
fmt.Fprintf(os.Stdout, "%s", "start connection")
http.Serve(lis,nil) }
|
rpc客户端:
$GOPATH/src/leitty/rpc/netrpc/server/rpc_client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package main
import ( "fmt" "net/rpc" )
type ArithRequest struct { A int B int } type ArithResponse struct { Pro int Quo int Rem int }
func main() { conn, err := rpc.DialHTTP("tcp","127.0.0.1:8095") if err != nil { panic(err) }
req := ArithRequest{9,2} var res ArithResponse
err = conn.Call("Arith.Multiply",req, &res) if err != nil { panic(err) } fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
}
|
net/rpc/jsonrpc库
使用net/rpc/jsonrpc库实现乘除法的rpc
$GOPATH/src/leitty/rpc/jsonrpc/server/rpc_server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package main
import ( "errors" "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" "os" )
// 算数运算结构体 type Arith struct { }
// 算数运算请求结构体 type ArithRequest struct { A int B int }
// 算数运算响应结构体 type ArithResponse struct { Pro int // 乘积 Quo int // 商 Rem int // 余数 }
// 乘法运算方法 func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error { res.Pro = req.A * req.B return nil }
// 除法运算方法 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)) // 注册rpc服务
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() // 接收客户端连接请求 if err != nil { continue }
go func(conn net.Conn) { // 并发处理客户端请求 fmt.Fprintf(os.Stdout, "%s", "new client in coming\n") jsonrpc.ServeConn(conn) }(conn) } }
|
$GOPATH/src/leitty/rpc/jsonrpc/server/rpc_client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package main
import ( "fmt" "log" "net/rpc/jsonrpc" )
// 算数运算请求结构体 type ArithRequest struct { A int B int }
// 算数运算响应结构体 type ArithResponse struct { Pro int // 乘积 Quo int // 商 Rem int // 余数 }
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) // 乘法运算 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) }
|
jsonrpc能实现跨语言的rpc通信,但是效率较低。一些第三方的rpc库都选择采用protobuf进行数据编码。
proto rpc库
使用protorpc库实现乘除法的rpc
protobuf的介绍,请移步这里
$GOPATH/src/leitty/rpc/protobuf/server/pb/arith.proto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| syntax = "proto3"; package pb;
// 算术运算请求结构 message ArithRequest { int32 a = 1; int32 b = 2; }
// 算术运算响应结构 message ArithResponse { int32 pro = 1; // 乘积 int32 quo = 2; // 商 int32 rem = 3; // 余数 }
// rpc方法 service ArithService { rpc multiply (ArithRequest) returns (ArithResponse); // 乘法运算方法 rpc divide (ArithRequest) returns (ArithResponse); // 除法运算方法 }
|
$GOPATH/src/leitty/rpc/protobuf/server/rpc_server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package main
import ( "errors" "test/rpc/pb" )
// 算术运算结构体 type Arith struct { }
// 乘法运算方法 func (this *Arith) Multiply(req *pb.ArithRequest, res *pb.ArithResponse) error { res.Pro = req.GetA() * req.GetB() return nil }
// 除法运算方法 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)) }
|
$GOPATH/src/leitty/rpc/protobuf/server/rpc_client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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) }
|
参考:
https://studygolang.com/articles/14336