GRPC - Hello World

尝试定义一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息, 流程如下:

  1. 编写.proto描述文件
  2. 编译生成.pb.go文件
  3. 服务端实现约定的接口并提供服务
  4. 客户端按照约定调用方法请求服务

项目目录:

1
2
3
4
5
6
7
8
9
10
tree $GOPATH/src/liusha.me/example/

├── hello
│   ├── client
│   │   └── main.go // 客户端
│   └── server
│   └── main.go // 服务端
└── proto
├── hello.pb.go // proto描述文件
└── hello.proto // proto编译后文件

描述文件:

proto/hello.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syntax = "proto3";         // 指定proto版本
package proto; // 指定包名

service Hello { // 定义Hello服务
rpc SayHello(HelloRequest) returns (HelloReply) {}
// 定义SayHello方法
}

message HelloRequest { // HelloRequest 请求结构
string name = 1;
}

message HelloReply { // HelloReply 响应结构
string message = 1;
}

hello.proto 文件中定义了一个 Hello Service ,该服务包含一个 SayHello 方法,同时声明了 HelloRequestHelloReply 消息结构用于请求和响应。客户端使用 HelloRequest 参数调用 SayHello 方法请求服务端,服务端响应 HelloReply 消息。protobuf 的语法后面会详细介绍,这里先不用过多纠结。

编译生成 .pb.go 文件:

1
protoc -I . --go_out=plugins=grpc:. ./proto/hello.proto

生成的 .pb.go 文件,按照.proto文件中的说明,包含服务端接口HelloServer描述,客户端接口及实现HelloClient,及HelloRequestHelloResponse` 结构体,不要手动编辑该文件

实现服务端接口:

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

import (
"os"
"fmt"
"net"

// 引入编译生成的包
pb "liusha.me/example/proto"

"golang.org/x/net/context"
"google.golang.org/grpc"
)

const (
// Address gRPC服务地址
Address = "127.0.0.1:50052"
)

// 定义helloService并实现约定的接口
type helloService struct{}

// HelloService ...
var HelloService = helloService{}

func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
resp := new(pb.HelloReply)
resp.Message = "Hello " + in.Name + "."

return resp, nil
}

func main() {
listen, err := net.Listen("tcp", Address)
if err != nil {
fmt.Println("failed to listen:", err)
os.Exit(1)
}

// 实例化grpc Server
s := grpc.NewServer()

// 注册HelloService
pb.RegisterHelloServer(s, HelloService)

fmt.Println("Listen on " + Address)

s.Serve(listen)
}

运行

1
2
// 服务端将开启并监听50051端口
go run hello/server/main.go

服务端引入编译后的 proto 包,实现约定的接口,接口描述可以查看 hello.pb.go 文件中的 HelloServer 接口描述。实例化 grpc Server 并注册 HelloService ,开始提供服务。

实现客户端调用

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

import (
"os"
"fmt"

// 引入proto包
pb "liusha.me/example/proto"

"golang.org/x/net/context"
"google.golang.org/grpc"
)

const (
// Address gRPC服务地址
Address = "127.0.0.1:50052"
)

func main() {
// 连接
conn, err := grpc.Dial(Address, grpc.WithInsecure())

if err != nil {
fmt.Println(err)
os.Exit(1)
}

defer conn.Close()

// 初始化客户端
c := pb.NewHelloClient(conn)

// 调用方法
reqBody := new(pb.HelloRequest)
reqBody.Name = "gRPC"
r, err := c.SayHello(context.Background(), reqBody)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

fmt.Println(r.Message)
}

运行:

1
2
go run hello/client/main.go
Hello gRPC.

客户端初始化连接后直接调用声明的方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。