Quickstart

rpc:Remote Procedure Call

gRPC 需要满足的条件

1
func (t *T) MethodName(argType T1, replyType *T2) error

即需要满足以下 5 个条件:

  • 方法类型(T)是导出的(首字母大写)
  • 方法名(MethodName)是导出的
  • 方法有2个参数(argType T1, replyType *T2),均为导出/内置类型
  • 方法的第2个参数一个指针(replyType *T2)
  • 方法的返回值类型是 error

导出的:”首字母大写“ 就是”导出的“

net/rpc 对参数个数的限制比较严格,仅能有2个,第一个参数是调用者提供的请求参数,第二个参数是返回给调用者的响应参数,也就是说,服务端需要将计算结果写在第二个参数中。如果调用过程中发生错误,会返回 error 给调用者。

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
// server/main.go
package main

import (
"log"
"net/http"
"net/rpc"
)

type Result struct {
Num, Ans int
}

type Cal int

func (cal *Cal) Square(num int, result *Result) error {
result.Num = num
result.Ans = num * num
return nil
}

func main() {
rpc.Register(new(Cal)) // 发布方法
rpc.HandleHTTP() // 注册 handler

log.Printf("Serving RPC server on port %d", 1234)
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("Error serving: ", err)
}
}

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
// client/main.go
package main

import (
"log"
"net/rpc"
)

type Result struct {
Num, Ans int
}

// 同步
// func main() {
// client, _ := rpc.DialHTTP("tcp", "localhost:1234")
// var result Result
// if err := client.Call("Cal.Square", 12, &result); err != nil {
// log.Fatal("Failed to call Cal.Square. ", err)
// }
// log.Printf("%d^2 = %d", result.Num, result.Ans)
// }

// 异步
func main() {
client, _ := rpc.DialHTTP("tcp", "localhost:1234")
var result Result
asyncCall := client.Go("Cal.Square", 12, &result, nil)
log.Printf("%d^2 = %d", result.Num, result.Ans)

<-asyncCall.Done
log.Printf("%d^2 = %d", result.Num, result.Ans)

}

启动执行

1
2
3
4
5
6
7
8
9
// server side
go mod tidy main
go run main.go
2021/07/01 14:12:06 Serving RPC server on port 1234

// client side
go run main.go
2021/07/01 14:14:08 0^2 = 0
2021/07/01 14:14:08 12^2 = 144