这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
Protobuf 是 Protocol Buffers 的简称,是一种与语言、平台无关,可扩展的序列化结构化数据的数据描述语言,Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。
基本语法
hello.proto 文件
1 | go复制代码syntax = "proto3"; |
- 第一行声明使用 proto3 语法。否则,默认使用 proto2 语法,目前主流推荐使用 v3 版本。此声明必须是文件的非空、非注释的第一行。
- package 指令指明当前是 main 包,用户也可以针对不同的语言定制对应的包路径和名称。
- message 关键字定义一个 String 类型消息体,在最终生成的Go语言代码中对应一个 String 结构体。每一个消息体的字段包含三个属性:类型、字段名称、字段编号。在消息体的定义上,除类型以外均不可重复。此处 String 类型中只有一个字符串类型的 value 成员,该成员编码时用1编号代替名字。
- Protobuf 中最基本的数据单元是 message,类似 Go 语言中的结构体。在 message 中可以嵌套 message 或其它的基础数据类型的成员。
关于标识号
消息体中字段定义了唯一的数字值。这些数字是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。
最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]的标识号, Protobuf 协议实现中对这些进行了预留。如果非要在 .proto 文件中使用这些预留标识号,编译时就会报警。类似地,你不能使用之前保留的任何标识符。
添加注释
.proto 文件添加注释,可以使用C/C++风格的 // 和 /* … */ 语法格式
保留字段
如果从前面定义的消息中删除了 和 字段,应保留其字段编号,使用关键字 reserved
:
1 | go复制代码syntax "proto3"; |
还可以将 reserved 关键字用作将来可能添加的字段的占位符。 可以使用 to
关键字将连续字段号占位。
1 | go复制代码syntax "proto3"; |
生成相应的Go代码
Protobuf 核心的工具集是 C++ 语言开发的,官方的 protoc 编译器中并不支持Go语言。要想基于上面
的 hello.proto 文件生成相应的Go代码,需要安装相应的插件。
- 安装官方的 protoc 工具,可以从 github.com/google/prot… 下载。
- 安装针对Go语言的代码生成插件,通过 go get github.com/golang/protobuf/protoc-gen-go 命令安装。
通过以下命令生成相应的Go代码:
$ protoc --go_out=. hello.proto
- go_out 参数告知 protoc 编译器去加载对应的 protoc-gen-go 工具,生成的代码放到当前目录。最后是一系列要处理的protobuf文件的列表。
- plugins=plugin1+plugin2:指定要加载的子插件列表,我们定义的 proto 文件是涉及了 RPC 服务的,而默认是不会生成 RPC 代码的,因此需要在 go_out 中给出 plugins 参数传递给 protoc-gen-go,告诉编译器,请支持 RPC(这里指定了内置的 grpc 插件)。
基本数据类型
protobuf 所生成出来的数据类型并非与原始的类型完全一致,下面是一些常见的类型映射:
生成的 hello.pb.go 文件
pb.go 文件是对 proto 文件所生成的对应的 Go 代码,在实际应用中将会引用到此文件。
1 | go复制代码// Code generated by protoc-gen-go. DO NOT EDIT. |
- String 类型自动生成了一组方法,其中 ProtoMessage 方法表示这是一个实现了 proto.Message 接口的方法。此外 Protobuf 还为每个成员生成了一个Get方法,能够提供便捷的取值方式,并且处理了一些空指针取值的情况,还能够通过 Reset 方法来重置该参数。
- .pb.go 文件的初始化方法,注意 fileDescriptor 的相关语句。fileDescriptor_61ef911816e0a8ce 表示是一个经过编译后的 proto 文件,是对 proto 文件的整体描述,其包含了 proto 文件名、引用(import)内容、包(package)名、选项设置、所有定义的消息体(message)、所有定义的枚举(enum)、所有定义的服务( service)、所有定义的方法(rpc method)等等内容。
- 每一个 Message Type 中都包含了 Descriptor 方法,Descriptor 代指对一个消息体(message)定义的描述,而这一个方法则会在 fileDescriptor 中寻找属于自己 Message Field 所在的位置再进行返回。
Protobuf 和 RPC组合
基于 String 类型,重新实现 HelloService 服务
1 | go复制代码package main |
下面是客户端请求HelloService服务的代码 client.go:
1 | go复制代码package main |
开启服务器端,开启客户端。客户端的执行结果如下:
1 | go复制代码$ go run client.go |
本文转载自: 掘金