go-rpc编程
RPC(remote procedure call protocol)-远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解低层网络技术的协议。它假定某些传输协议的存在,如TCP或UDP,以便为通信程序之间携带信息数据。通过它可以使得函数调用模式网络化。
1.RPC工作原理
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
- 调用客户端句柄,执行传送参数
- 调用本地系统内核发送网络消息
- 消息传送到远程主机
- 服务器句柄得到消息并取得参数
- 执行远程过程
- 执行的过程将结果返回服务器句柄
- 服务器句柄返回结果,调用远程系统内核
- 消息传回本地主机
- 客户句柄由内核接收消息
- 客户端接收句柄返回的数据
2.go实现RPC调用
go标准库提供了对RPC的支持,并支持三个级别的RPC:TCP、HTTP、JSONRPC。但是go的rpc包只支持go开发的服务器与客户端之间的交互,因为在内部,他们采用了Gob来编码。
go rpc的函数只有符合下面的条件才能被远程访问:
- 函数必须是导出的(首字母大写)
- 必须有两个导出类型的参数,第一个参数是接收的参数,第二个参数是返回给客户端的参数,且第二个参数必须是指针类型的
- 函数还要有一个返回值error
1
| func (t *T) MethodName(arg1 Arg1Type, arg2 *Arg2Type) error
|
2.1.HTTP RPC
2.1.1. HTTP RPC Server
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
| package main
import ( "net/http" "net/rpc" )
func main() { myMath := new(MyMath) rpc.Register(myMath) rpc.HandleHTTP()
http.ListenAndServe(":8080", nil) }
type MyMath int
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
func (m *MyMath) Add(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X + arg1.Y arg2.Msg = "hello world!" return nil }
func (m *MyMath) Minus(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X - arg1.Y arg2.Msg = "hello mebaron!" return nil }
|
2.1.2.HTTP RPC Client
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
| package main
import ( "fmt" "log" "net/rpc" )
func main() { client, _ := rpc.DialHTTP("tcp", "127.0.0.1:8080")
query := Arg1{ X: 12, Y: 6, } res := Arg2{}
client.Call("MyMath.Add", query, &res) fmt.Println(res.Answer, res.Msg)
res2 := Arg2{} client.Call("MyMath.Minus", query, &res2) fmt.Println(res2.Answer, res2.Msg) }
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
|
2.2.TCP RPC
2.2.1.TCP RPC Server
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
| package main
import ( "log" "net" "net/rpc" )
func main() { myMath := new(MyMath) rpc.Register(myMath)
tcpAddr, _ := net.ResolveTCPAddr("tcp", ":8080") listener, _ := net.ListenTCP("tcp4", tcpAddr)
for { conn, err := listener.Accept() if err != nil { log.Fatal(err) }
go rpc.ServeConn(conn) } }
type MyMath int
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
func (m *MyMath) Add(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X + arg1.Y arg2.Msg = "hello world!" return nil }
func (m *MyMath) Minus(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X - arg1.Y arg2.Msg = "hello mebaron!" return nil }
|
2.2.2.TCP RPC Client
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
| package main
import ( "fmt" "log" "net/rpc" )
func main() { client, _ := rpc.Dial("tcp", "127.0.0.1:8080")
query := Arg1{ X: 12, Y: 6, } res := Arg2{}
client.Call("MyMath.Add", query, &res) fmt.Println(res.Answer, res.Msg)
res2 := Arg2{} client.Call("MyMath.Minus", query, &res2) fmt.Println(res2.Answer, res2.Msg) }
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
|
2.3.JSON RPC
2.3.1.JSON RPC Server
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 ( "log" "net" "net/rpc" "net/rpc/jsonrpc" )
func main() { myMath := new(MyMath) rpc.Register(myMath)
tcpAddr, _ := net.ResolveTCPAddr("tcp", ":8080") listener, _ := net.ListenTCP("tcp4", tcpAddr)
for { conn, err := listener.Accept() if err != nil { log.Fatal(err) }
go jsonrpc.ServeConn(conn) } }
type MyMath int
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
func (m *MyMath) Add(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X + arg1.Y arg2.Msg = "hello world!" return nil }
func (m *MyMath) Minus(arg1 Arg1, arg2 *Arg2) error { arg2.Answer = arg1.X - arg1.Y arg2.Msg = "hello mebaron!" return nil }
|
2.3.2.JSON RPC Client
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
| package main
import ( "fmt" "log" "net/rpc/jsonrpc" )
func main() { client, err := jsonrpc.Dial("tcp", "127.0.0.1:8080") if err != nil { log.Fatal(err) }
query := Arg1{ X: 12, Y: 6, } res := Arg2{}
err = client.Call("MyMath.Add", query, &res) if err != nil { log.Fatal(err) } fmt.Println(res.Answer, res.Msg)
res2 := Arg2{} err = client.Call("MyMath.Minus", query, &res2) if err != nil { log.Fatal(err) } fmt.Println(res2.Answer, res2.Msg) }
type Arg1 struct { X, Y int }
type Arg2 struct { Answer int Msg string }
|