Saki's 研究记录

gRPC hello world

字数统计: 1.1k阅读时长: 5 min
2024/01/03

开发环境

OS: mac OS 14.2.1 (23C71)
Golang: go version go1.21.0 darwin/arm64
Protoc: libprotoc 3.20.3

Hello World Demo

创建目录

1
2
3
mkdir -p go-grpc/helloworld
cd go-grpc
go mod init go-grpc

编写 Protocol Buffers 协议文件

创建 helloworld/helloworld.proto 文件并编辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// gRPC: helloworld demo
syntax = "proto3";

option go_package = "./helloworld";

// The greeting service definition.
service Greeter {
// Send a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The resquest message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings.
message HelloReply {
string message = 1;
}

将 Protocol Buffers 文件转换为 Go 语言的源代码文件

使用以下命令将helloworld/helloworld.proto转换成go语言源代码文件:

1
protoc -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. helloworld/helloworld.proto
  • protoc:这是 Protocol Buffers 编译器的命令,用于编译 .proto 文件。
  • -I .:指定了编译器查找导入文件的目录,. 表示当前目录。
  • –go_out=.:这部分指令告诉编译器生成 Go 语言的代码,并将输出文件放在当前目录中。
  • –go-grpc_out=require_unimplemented_servers=false:.:这部分指令告诉编译器生成支持 gRPC 的 Go 语言代码,并且在生成的代码中禁用未实现的服务器检查。同样,输出文件也会放在当前目录中。
  • helloworld/helloworld.proto:这是要编译的 Protocol Buffers 文件路径。

执行完成后会生成一个:helloworld.pb.gohelloworld.proto 文件, 目录结构如下:

1
2
3
4
helloworld
├── helloworld.pb.go
├── helloworld.proto
└── helloworld_grpc.pb.go

编写 gRPC 服务端的代码

在工程目录下创建并编辑 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
/*
* 说明:gRPC: helloworld demo
* 作者:sakishum
* 时间:2023-12-30 23:24 PM
* 更新:
*/
package main

import (
"context"
"log"
"net"

"google.golang.org/grpc"
"google.golang.org/grpc/reflection"

pb "go-grpc/helloworld"
)

const port = "0.0.0.0:50051"

type server struct {
pb.UnimplementedGreeterServer // 必须加的结构体
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to server: %v", err)
}
}

编写 gRPC 客户端的代码

在工程目录下创建并编辑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
44
45
46
/*
* 说明:gRPC: helloworld demo
* 作者:sakishum
* 时间:2023-12-30 23:24 PM
* 更新:
*/
package main

import (
"context"
"log"
"os"
"time"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

pb "go-grpc/helloworld"
)

const (
address = "localhost:50051"
defaultName = "world"
)

func main() {
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)

name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %v", r.Message)
}

Try it out!

在工程目录下执行以下命令运行服务端:

1
2
> go mod tidy
> go run server/main.go

开启一个新的终端并启动客户端,如果没有报错可以看到有调用成功的结果输出:

1
2
> go run client/main.go
2023/12/31 23:50:15 Greeting: Hello world

协议变更

gRPC 协议旨在支持随时间变化的服务。 通常,gRPC 服务和方法会不中断地新增内容。

更新 gRPC 服务

helloworld/helloworld.proto 再添加一个方法:SayHelloAgain, requestresponse 类型同 SayHello 函数。

1
2
3
4
5
6
7
8
...
// The greeting service definition.
service Greeter {
...
// Sends another greeting
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
...

重新生成 gRPC 代码

重新将变更后的helloworld/helloworld.proto转换成go语言源代码文件:

1
protoc -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. helloworld/helloworld.proto

更新服务端

编辑 server/main.go 文件,添加以下代码:

1
2
3
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello again " + in.Name}, nil
}

更新客户端

编辑 client/main.go 文件,添加以下代码:

1
2
3
4
5
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)

运行

执行以下命令重跑服务端:

1
> go run server/main.go

启动客户端,可以看到新方法调用成功的结果输出:

1
2
3
> go run client/main.go
2023/12/31 00:08:44 Greeting: Hello world
2023/12/31 00:08:44 Greeting: Hello again world

引用

以上。

CATALOG
  1. 1. 开发环境
  2. 2. Hello World Demo
    1. 2.1. 创建目录
    2. 2.2. 编写 Protocol Buffers 协议文件
    3. 2.3. 将 Protocol Buffers 文件转换为 Go 语言的源代码文件
    4. 2.4. 编写 gRPC 服务端的代码
    5. 2.5. 编写 gRPC 客户端的代码
    6. 2.6. Try it out!
  3. 3. 协议变更
    1. 3.1. 更新 gRPC 服务
    2. 3.2. 重新生成 gRPC 代码
    3. 3.3. 更新服务端
    4. 3.4. 更新客户端
    5. 3.5. 运行
  4. 4. 引用