Cwww3's Blog

Record what you think

0%

protobuf

Protocol Buffers

message

1
2
3
4
5
6
7
syntax = "proto3";

message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
  • 文件后缀.proto
  • 第一行定义版本 默认 proto2
  • 唯一的数字编号用于识别在消息二进制格式中的字段。一旦被使用,不能更改。1-15只需要一个字节进行编码,用于标识频繁使用的字段。数字编号有一定的范围,且有一些预定义的数字。
  • proto3 字段默认是 singular 的,标识0个或1个。repeated 标识0个或多个
  • 一个.proto 文件中可以定义多个消息类型。
  • 可以通过import 引入定义在其他.proto 文件中的消息类型
  • 注释格式 当行// 多行/*...*/

更新消息类型

  • 如果因为更新删除一个字段或将其直接注释掉,这些被删掉的字段可能被重新使用,加载老版本的数据时,可能会造成数据冲突。

  • 可以通过指定保留你删除的字段的编号或名字,这样当使用该数字编号时,编译器会报错。(JSON序列化可能会有问题)

1
2
3
4
message Foo {
reserved 2, 15, 9 to 11; // 不能在同一个保留语句中(reserved)混合使用编号和字段名
reserved "foo", "bar";
}

安装Protoc

installed the compiler

下载地址 选择对应平台,解压并配置PATH路径

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protoc [OPTION] PROTO_FILES
# PROTO_FILES 指定被编译的文件
# 这些文件应该被包含在-I指定的PATH中,以便编译器可以确定其规范名称
# Although the files are named relative to the current directory, each file must reside in one of the IMPORT_PATHs so that the compiler can determine its canonical name.

-I=PATH, --proto_path=PATH # PATH specifies a directory in which to look for .proto files when resolving import directives.
# 指定所依赖的.protoc文件所在目录,可以指定多次,默认当前路径

# 注意!!! PATH是前缀 如果.proto文件中的import路径是3/4.proto 那么PATH应该为1/2
-java_out=OUT_DIR #生成的JAVA文件放置在OUT_DIR目录下

# Java 文件生成
protoc --java_out=./java/ ./proto/helloworld.proto
# 将proto目录下的helloworld.proto文件编译生成的文件放在java目录下

# Go 文件生成
# protoc 没有内置go语言对应的编译器 需要安装protoc-gen-go插件

install the Go protocol buffers plugin

1
2
# 自带grpc插件
go get -u github.com/golang/protobuf/protoc-gen-go
  • google(新)
1
2
3
# 不支持grpc
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
或 go install google.golang.org/protobuf/cmd/protoc-gen-go@latest (go1.16)
  • 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-go_out=OUT_DIR #指定输出的go文件所在目录

--go_opt=FLAG #在使用protoc时,通过go_opt给protoc-gen-go传特定的参数

paths=source_relative # the output file is placed in the same relative directory as the input file.

paths=import # 会根据Go package's import path创建对应的文件层级

protoc --go_out=out --go_opt=paths=source_relative foo.proto

# 比如 foo.proto 在当前目录下,那么生成文件在out/foo.pb.go

protoc --go_out=out --go_opt=paths=source_relative in/foo.proto
# 如果foo.proto在in目录下,那么生成文件在out/in/foo.pb.go

# --go_out=DIR DIR目录需要在执行命令之前被创建出来,而后续的in目录会自动创建

  • package
1
2
3
option go_package = "example.com/project/protos/fizz";
# 生成的go文件默认使用fizz当做包名,如果需要显示声明,通过;间隔
option go_package = "example.com/project/protos/fizz;package_name";

生成go文件代码

GRPC

  • 是rpc的框架

  • 服务类型

    • 一元RPC
    • 服务端流式RPC
    • 客服端流式RPC
    • 双向流式 RPC
  • 在服务器端,服务器实现服务声明的方法并运行 gRPC 服务器来处理客户端调用。gRPC 基础设施解码传入请求、执行服务方法并编码服务响应。

  • 在客户端,客户端有一个称为stub的本地对象,它实现与服务相同的方法(由proto生成的文件)。然后客户端可以在本地对象上调用这些方法,将调用的参数包装在适当的协议缓冲区消息类型中 ,gRPC 负责将请求发送到服务器并返回服务器的协议缓冲区响应。

生成

当使用编译器编译.proto 文件时,编译器会生成你所选择的语言生成代码,对于Go,会生成一个.pb.go文件,里面包含.proto中的定义的每一个类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 生成Go类型文件需要在.proto文件中通过 go_package 指定包名   
# 例 option go_package = "./;packagename";

# --go_out指定.pb.go文件生成位置
protoc --go_out=. *.proto

# 生成grpc文件(github版本)
protoc --go_out=plugin=grpc:. *.proto

# 生成grpc文件(google版本)
# 安装protoc-gen-go-grpc插件
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
# --go-grpc_out 指定grpc生成路径
protoc --go_out=. --go-grpc_out=. *.proto

gRPC-gateway

gRPC-Gateway 是Google protocol buffers compiler(protoc)的一个插件。读取 protobuf 定义然后生成反向代理服务器,将 RESTful HTTP API 转换为 gRPC。

  • 安装
1
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
  • 修改 .proto 文件
1
2
3
4
5
6
7
8
9
10
11
12
// 添加
import "google/api/annotations.proto";

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
// 定义HTTP对应的方法和路径
option (google.api.http) = {
get: "/v1/greeter/sayhello"
body: "*"
};
}
}
  • 编译
1
2
--grpc-gateway_out
--grpc-gateway_opt
  • swag文档
1
2
3
# 生成swag文档插件
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
--openapiv2_out

示例

示例代码

Donate comment here.