1 Protocol Buffers 简介
1 Protocol Buffers 简介
protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储的,占用空间小,但也带来了可读性差的缺点。protobuf 在通信协议和数据存储等领域应用广泛。例如著名的分布式缓存工具 Memcached 的 Go 语言版本groupcache 就使用了 protobuf 作为其 RPC 数据格式。
Protobuf 在 .proto
定义需要处理的结构化数据,可以通过 protoc
工具,将 .proto
文件转换为 C、C++、Golang、Java、Python 等多种语言的代码,兼容性好,易于使用。
2 安装
2.1 protoc
从 Protobuf Releases 下载最先版本的发布包安装。如果是 Ubuntu,可以按照如下步骤操作(以3.11.2为例)。
1
|
# 下载安装包
|
如果不想安装在 /usr/local 目录下,可以解压到其他的其他,并把解压路径下的 bin 目录 加入到环境变量即可。
如果能正常显示版本,则表示安装成功。
1
|
$ protoc --version
|
2.2 protoc-gen-go
我们需要在 Golang 中使用 protobuf,还需要安装 protoc-gen-go,这个工具用来将 .proto
文件转换为 Golang 代码。
1
|
go get -u github.com/golang/protobuf/protoc-gen-go
|
protoc-gen-go 将自动安装到 $GOPATH/bin
目录下,也需要将这个目录加入到环境变量中。
3 定义消息类型
接下来,我们创建一个非常简单的示例,student.proto
1
|
syntax = "proto3";
|
在当前目录下执行:
1
|
$ protoc --go_out=. *.proto
|
即是,将该目录下的所有的 .proto 文件转换为 Go 代码,我们可以看到该目录下多出了一个 Go 文件 student.pb.go。这个文件内部定义了一个结构体 Student,以及相关的方法:
1
|
type Student struct {
|
逐行解读student.proto
- protobuf 有2个版本,默认版本是 proto2,如果需要 proto3,则需要在非空非注释第一行使用
syntax = "proto3"
标明版本。 package
,即包名声明符是可选的,用来防止不同的消息类型有命名冲突。- 消息类型 使用
message
关键字定义,Student 是类型名,name, male, scores 是该类型的 3 个字段,类型分别为 string, bool 和 []int32。字段可以是标量类型,也可以是合成类型。 - 每个字段的修饰符默认是 singular,一般省略不写,
repeated
表示字段可重复,即用来表示 Go 语言中的数组类型。 - 每个字符
=
后面的数字称为标识符,每个字段都需要提供一个唯一的标识符。标识符用来在消息的二进制格式中识别各个字段,一旦使用就不能够再改变,标识符的取值范围为 [1, 2^29 - 1] 。 - .proto 文件可以写注释,单行注释
//
,多行注释/* ... */
- 一个 .proto 文件中可以写多个消息类型,即对应多个结构体(struct)。
接下来,就可以在项目代码中直接使用了,以下是一个非常简单的例子,即证明被序列化的和反序列化后的实例,包含相同的数据。
1
|
package main
|
- 保留字段(Reserved Field)
更新消息类型时,可能会将某些字段/标识符删除。这些被删掉的字段/标识符可能被重新使用,如果加载老版本的数据时,可能会造成数据冲突,在升级时,可以将这些字段/标识符保留(reserved),这样就不会被重新使用了,protoc 会检查。
1
|
message Foo {
|
4 字段类型
4.1 标量类型(Scalar)
proto类型 | go类型 | 备注 | proto类型 | go类型 | 备注 |
---|---|---|---|---|---|
double | float64 | float | float32 | ||
int32 | int32 | int64 | int64 | ||
uint32 | uint32 | uint64 | uint64 | ||
sint32 | int32 | 适合负数 | sint64 | int64 | 适合负数 |
fixed32 | uint32 | 固长编码,适合大于2^28的值 | fixed64 | uint64 | 固长编码,适合大于2^56的值 |
sfixed32 | int32 | 固长编码 | sfixed64 | int64 | 固长编码 |
bool | bool | string | string | UTF8 编码,长度不超过 2^32 | |
bytes | []byte | 任意字节序列,长度不超过 2^32 |
标量类型如果没有被赋值,则不会被序列化,解析时,会赋予默认值。
- strings:空字符串
- bytes:空序列
- bools:false
- 数值类型:0
4.2 枚举(Enumerations)
枚举类型适用于提供一组预定义的值,选择其中一个。例如我们将性别定义为枚举类型。
1
|
message Student {
|
- 枚举类型的第一个选项的标识符必须是0,这也是枚举类型的默认值。
- 别名(Alias),允许为不同的枚举值赋予相同的标识符,称之为别名,需要打开
allow_alias
选项。
1
|
message EnumAllowAlias {
|
4.3 使用其他消息类型
Result
是另一个消息类型,在 SearchReponse 作为一个消息字段类型使用。
1
|
message SearchResponse {
|
嵌套写也是支持的:
1
|
message SearchResponse {
|
如果定义在其他文件中,可以导入其他消息类型来使用:
1
|
import "myproject/other_protos.proto";
|
4.4 任意类型(Any)
Any 可以表示不在 .proto 中定义任意的内置类型。
1
|
import "google/protobuf/any.proto";
|
4.5 oneof
1
|
message SampleMessage {
|
4.6 map
1
|
message MapRequest {
|
5 定义服务(Services)
如果消息类型是用来远程通信的(Remote Procedure Call, RPC),可以在 .proto 文件中定义 RPC 服务接口。例如我们定义了一个名为 SearchService 的 RPC 服务,提供了 Search
接口,入参是 SearchRequest
类型,返回类型是 SearchResponse
1
|
service SearchService {
|
官方仓库也提供了一个插件列表,帮助开发基于 Protocol Buffer 的 RPC 服务。
6 protoc 其他参数
命令行使用方法
1
|
protoc --proto_path=IMPORT_PATH --<lang>_out=DST_DIR path/to/file.proto
|
--proto_path=IMPORT_PATH
:可以在 .proto 文件中 import 其他的 .proto 文件,proto_path 即用来指定其他 .proto 文件的查找目录。如果没有引入其他的 .proto 文件,该参数可以省略。--<lang>_out=DST_DIR
:指定生成代码的目标文件夹,例如 –go_out=. 即生成 GO 代码在当前文件夹,另外支持 cpp/java/python/ruby/objc/csharp/php 等语言
7 推荐风格
-
文件(Files)
- 文件名使用小写下划线的命名风格,例如 lower_snake_case.proto
- 每行不超过 80 字符
- 使用 2 个空格缩进
-
包(Packages)
- 包名应该和目录结构对应,例如文件在
my/package/
目录下,包名应为my.package
- 包名应该和目录结构对应,例如文件在
-
消息和字段(Messages & Fields)
- 消息名使用首字母大写驼峰风格(CamelCase),例如
message StudentRequest { ... }
- 字段名使用小写下划线的风格,例如
string status_code = 1
- 枚举类型,枚举名使用首字母大写驼峰风格,例如
enum FooBar
,枚举值使用全大写下划线隔开的风格(CAPITALS_WITH_UNDERSCORES ),例如 FOO_DEFAULT=1
- 消息名使用首字母大写驼峰风格(CamelCase),例如
-
服务(Services)
- RPC 服务名和方法名,均使用首字母大写驼峰风格,例如
service FooService{ rpc GetSomething() }
- RPC 服务名和方法名,均使用首字母大写驼峰风格,例如
附:参考
原文地址:https://www.cnblogs.com/cheyunhua/p/15305046.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android 功耗(3)---高通功耗问题分析方法
- 搞定Java快速排序
- Android 功耗(4)---MTK平台待机功耗分析流程
- 使用iframe实现在pc端预览移动端页面的效果
- html+css实现彩色渐变滑动条
- 项目实战 01:将唐诗三百首写入 Elasticsearch 会发生什么?
- 装饰器和代理模式的区别,从一碗小米粥谈起
- EasyNVR部署在centos虚拟机上出现无法访问情况,该如何排查?
- RTSP协议网页无插件直播平台EasyNVR视频广场无法搜索纯数字关键词的通道,该如何解决的?
- RTSP协议网页无插件直播平台EasyNVR通道管理搜索纯数字关键词,返回结果为空如何解决?
- Mysql如何选择唯一索引和普通索引
- Python自学成才之路 多线程开发
- Mysql为什么会抖一下呢
- Python自学成才之路 线程间协作 lock,condition,event的使用
- Java 语言基础 (初识Java语言, 变量和数据类型, 运算符, 流程控制语句, 数组)