文章524
标签277
分类67

使用Protobuf3和grpc实现RPC调用

本文展示了通过Protobuf3定义服务接口并通过grpc实现简单的RPC调用

源代码:

https://github.com/JasonkayZK/Go_Learn/tree/protobuf_grpc_demo


使用Protobuf3和grpc实现RPC调用

使用Protobuf是还需要安装protoc和protoc-gen-go以支持通过.proto生成对应的接口;

下面以在windows下为例介绍protoc和protoc-gen-go的安装(protoc底层调用的是protoc-gen-go)

安装protoc和protoc-gen-go

protoc

Github的Release下载:

https://github.com/protocolbuffers/protobuf/releases

下载对应平台的压缩包(win/Linux);

解压之后将bin目录下的protoc复制到系统环境变量的路径下:

  • win: C:\Windows\System32
  • Linux: /bin

等等;

然后测试:

1
2
protoc --version
libprotoc 3.12.0

protoc-gen-go

Github的Release下载:

https://github.com/golang/protobuf/releases

下载压缩包并解压;

解压后在protoc-gen-go目录下编译:

1
go build

然后将生成的二进制文件protoc-gen-go复制到系统环境变量的路径下

全部安装完成!


目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tree /F                                       
.
│ go.mod
│ go.sum
│
├─hello_client
│ main.go
│
├─hello_server
│ main.go
│
└─proto
hello.pb.go
hello.proto

编写proto

本例子使用proto3语法;

hello.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 指定proto版本
syntax = "proto3";

package proto;

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

// 定义响应结构
message HelloResponse{
string message = 1;
}

// 定义Hello服务
service Hello{
// 定义服务中的方法
rpc SayHello (HelloRequest) returns (HelloResponse);
}

编写完成之后使用protoc生成.pb.gp文件:

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

即可生成hello.pb.go文件


初始化gomod项目

使用go mod init protobuf_grpc_demo初始化项目;

生成go.mod文件;

然后使用go mod tidy初始化;


服务端代码

hello_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
package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"net"
pb "protobuf_grpc_demo/proto"
)

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

type helloService struct{}

// 定义服务接口的SayHello的实现方法
func (s helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
resp := new(pb.HelloResponse)
fmt.Printf("Get remote call from client, the context is: %s\n\n", ctx)

resp.Message = "hello " + in.Name + "."
fmt.Printf("Response msg: " + resp.Message)

return resp, nil
}

var HelloService = helloService{}

func main() {
listen, err := net.Listen("tcp", Address)
if err != nil {
fmt.Printf("Failed to listen: %v", err)
}

// 实现gRPC Server
s := grpc.NewServer()

// 注册helloServer为客户端提供服务
// 内部调用了s.RegisterServer()
pb.RegisterHelloServer(s, HelloService)

println("Listen on: " + Address)

_ = s.Serve(listen)
}

客户端代码

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
package main

import (
"context"
"fmt"
"google.golang.org/grpc"
pb "protobuf_grpc_demo/proto"
)

const (
Address = "127.0.0.1:50052"
)

func main() {
// 连接gRPC服务器
conn, err := grpc.Dial(Address, grpc.WithInsecure())
if err != nil {
fmt.Printf("Failed to dial to: " + Address)
}
if conn == nil {
panic("Failed to get connection from the server")
}
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.Printf("Fail to call method, err: %v", err)
}

fmt.Println(r.Message)
}

运行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务端运行:
D:\workspace\go_learn>go run hello_server/main.go
Listen on: 127.0.0.1:50052

# 客户端运行:
D:\workspace\go_learn>go run hello_client/main.go
hello gRPC.

# 并且在服务器返回
Get remote call from client, the context is: context.Background.WithCancel.WithValue(type peer.peerKey, val <not Stri
nger>).WithValue(type metadata.mdIncomingKey, val <not Stringer>).WithValue(type grpc.streamKey, val <not Stringer>)

Response msg: hello gRPC.

附录

源代码:

https://github.com/JasonkayZK/Go_Learn/tree/protobuf_grpc_demo

如果觉得文章写的不错, 可以关注微信公众号: Coder张小凯

内容和博客同步更新~



本文作者:Jasonkay
本文链接:https://jasonkayzk.github.io/cn/2020/05/14/%E4%BD%BF%E7%94%A8Protobuf3%E5%92%8Cgrpc%E5%AE%9E%E7%8E%B0RPC%E8%B0%83%E7%94%A8/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可