
Go原生RPC
1.RPC概述
- Remote Procedure Call Protocol,通过网络从远程计算机程序上请求服务,隐藏了底层网络技术
- 是一个技术思想
1.1 调用过程
1.2 架构
- 客户端(Client):服务的调用方
- 服务端(Server):真正的服务提供者
- 客户端存根:存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方
- 服务端存根:接收客户端发送过来的消息,将消息解包,并调用本地的方法
1.3 RPC实现
- go原生rpc
- grpc:Google开源的rpc实现
- thrift:Facebook开源的跨语言的服务开发框架
- dubbo:阿里开源的rpc框架
2.Go内置RPC
Go标准包中已经提供了对RPC的支持,而且支持三个级别的RPC:TCP、HTTP、JSONRPC。但Go的RPC它只支持Go开发的服务器与客户端之间的交互,因为在内部,它们采用了Gob来编码。
Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:
- 函数必须是导出的(首字母大写)
- 必须有两个导出类型的参数,
- 第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数必须是指针类型的
- 函数还要有一个返回值error
举个例子,正确的RPC函数格式如下:
func (t *T) MethodName(argType T1, replyType *T2) error // T、T1和T2类型必须能被`encoding/gob`包编解码。任何的RPC都需要通过网络来传递数据,Go RPC可以利用HTTP和TCP来传递数据,利用HTTP的好处是可以直接复用
net/http里面的一些函数。
3.rpc式http
server代码
package main import ( "errors" "fmt" "net/http" "net/rpc" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } type Arith int func (t *Arith) Multiply(args *Args, reply *int) error { *reply = args.A * args.B return nil } func (t *Arith) Divide(args *Args, quo *Quotient) error { if args.B == 0 { return errors.New("divide by zero") } quo.Quo = args.A / args.B quo.Rem = args.A % args.B return nil } func main() { arith := new(Arith) rpc.Register(arith) rpc.HandleHTTP() err := http.ListenAndServe(":1234", nil) if err != nil { fmt.Println(err.Error()) } }client代码:
package main import ( "fmt" "log" "net/rpc" "os" ) // 客户端和服务端相同代码写在公共文件中,避免需要分别去修改 type Args struct { A, B int } type Quotient struct { Quo, Rem int } func main() { if len(os.Args) != 2 { fmt.Println("Usage: ", os.Args[0], "server") os.Exit(1) } serverAddress := os.Args[1] //rpc.DialHTTP 拨号 client, err := rpc.DialHTTP("tcp", serverAddress+":1234") if err != nil { log.Fatal("dialing:", err) } // Synchronous call args := Args{17, 8} var reply int err = client.Call("Arith.Multiply", args, &reply) //通过字符串调用"Arith.Multiply",类的名称和方法,通过字符串找到方法(type Arith int) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply) var quot Quotient err = client.Call("Arith.Divide", args, ") if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem) }运行结果:
gorpc\http\client> .\main.exe 127.0.0.1 Arith: 17*8=136 Arith: 17/8=2 remainder 1
4.rpc式tcp
service代码:
package main import ( "errors" "fmt" "net" "net/rpc" "os" "time" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } type Arith int func (t *Arith) Multiply(args *Args, reply *int) error { fmt.Printf("Multiply: %v\n", time.Now()) // time.Sleep(10 * time.Second) *reply = args.A * args.B return nil } func (t *Arith) Divide(args *Args, quo *Quotient) error { if args.B == 0 { return errors.New("divide by zero") } quo.Quo = args.A / args.B quo.Rem = args.A % args.B return nil } func main() { arith := new(Arith) rpc.Register(arith) tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234") checkError(err) listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err) for { fmt.Printf("accept at %v\n", time.Now()) conn, err := listener.Accept() if err != nil { continue } fmt.Printf("get at %v\n", time.Now()) go rpc.ServeConn(conn) } } func checkError(err error) { if err != nil { fmt.Println("Fatal error ", err.Error()) os.Exit(1) } }client代码:
package main import ( "fmt" "log" "net/rpc" "os" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } func main() { if len(os.Args) != 2 { fmt.Println("Usage: ", os.Args[0], "server:port") os.Exit(1) } service := os.Args[1] client, err := rpc.Dial("tcp", service) if err != nil { log.Fatal("dialing:", err) } // Synchronous call args := Args{17, 8} var reply int err = client.Call("Arith.Multiply", args, &reply) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply) var quot Quotient err = client.Call("Arith.Divide", args, ") if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem) }运行结果:
server: tcp\server>main.exe accept at 2020-12-20 20:51:04.7746436 +0800 CST m=+0.008995401 get at 2020-12-20 20:51:18.9825206 +0800 CST m=+14.216872401 accept at 2020-12-20 20:51:18.9835037 +0800 CST m=+14.217855501 Multiply: 2020-12-20 20:51:18.9844907 +0800 CST m=+14.218842501 get at 2020-12-20 20:52:14.9675121 +0800 CST m=+70.201863901 accept at 2020-12-20 20:52:14.9685095 +0800 CST m=+70.202861301 Multiply: 2020-12-20 20:52:14.9685095 +0800 CST m=+70.202861301 client: \tcp\client>client.exe 127.0.0.1:1212 Arith: 17*8=136 Arith: 17/8=2 remainder 1
