go-rpc编程

RPC(remote procedure call protocol)-远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解低层网络技术的协议。它假定某些传输协议的存在,如TCP或UDP,以便为通信程序之间携带信息数据。通过它可以使得函数调用模式网络化。

1.RPC工作原理

运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:

  1. 调用客户端句柄,执行传送参数
  2. 调用本地系统内核发送网络消息
  3. 消息传送到远程主机
  4. 服务器句柄得到消息并取得参数
  5. 执行远程过程
  6. 执行的过程将结果返回服务器句柄
  7. 服务器句柄返回结果,调用远程系统内核
  8. 消息传回本地主机
  9. 客户句柄由内核接收消息
  10. 客户端接收句柄返回的数据

2.go实现RPC调用

go标准库提供了对RPC的支持,并支持三个级别的RPC:TCP、HTTP、JSONRPC。但是go的rpc包只支持go开发的服务器与客户端之间的交互,因为在内部,他们采用了Gob来编码。

go rpc的函数只有符合下面的条件才能被远程访问:

  1. 函数必须是导出的(首字母大写)
  2. 必须有两个导出类型的参数,第一个参数是接收的参数,第二个参数是返回给客户端的参数,且第二个参数必须是指针类型的
  3. 函数还要有一个返回值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函数并交给HTTP处理
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() {
// 连接到rpc服务端
client, _ := rpc.DialHTTP("tcp", "127.0.0.1:8080")

query := Arg1{
X: 12,
Y: 6,
}
res := Arg2{}

// 远程调用rpc函数
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函数
rpc.Register(myMath)

// 启动TCP RPC服务
tcpAddr, _ := net.ResolveTCPAddr("tcp", ":8080")
listener, _ := net.ListenTCP("tcp4", tcpAddr)

for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}

// 将请求交给rpc处理
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() {
// 连接rpc服务端
client, _ := rpc.Dial("tcp", "127.0.0.1:8080")

query := Arg1{
X: 12,
Y: 6,
}
res := Arg2{}

// 远程调用rpc函数
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函数
rpc.Register(myMath)

// 启动TCP RPC服务
tcpAddr, _ := net.ResolveTCPAddr("tcp", ":8080")
listener, _ := net.ListenTCP("tcp4", tcpAddr)

for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}

// 将请求交给rpc处理
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() {
// 连接rpc服务端
client, err := jsonrpc.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}

query := Arg1{
X: 12,
Y: 6,
}
res := Arg2{}

// 远程调用rpc函数
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
}