protobuf的编码及序列化介绍我觉得这两篇还可以
一、简介
protobuf为Google开发的高性能序列化和反序列化工具
官方文档给出的定义和描述:
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
特点:
语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序
.protoc文件语法
例子AddressBook.proto
syntax = "proto2";
package tutorial;
option java_package = "com.simonwang.java.learn.protobufentities";
option java_outer_classname = "AddressBookProtos";
message Person{
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber{
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;
}
message AddressBook{
repeated Person people = 1;
}
定义形式:
//类名
message xxx {
字段规则 类型 名称 = 字段编号;
}
字段规则:
required -> 字段只能也必须出现 1 次
optional -> 字段可出现 0 次或1次
repeated -> 字段可出现任意多次(包括 0)用于集合
标量值类型
.proto Type | Notes | C++ Type | Java Type | Python Type[2] | Go Type |
---|---|---|---|---|---|
double | double | double | float | *float64 | |
float | float | float | float | *float32 | |
int32 | 使用可变长度编码。编码负数的效率低 - 如果你的字段可能有负值,请改用 sint32 | int32 | int | int | *int32 |
int64 | 使用可变长度编码。编码负数的效率低 - 如果你的字段可能有负值,请改用 sint64 | int64 | long | int/long[3] | *int64 |
uint32 | 使用可变长度编码 | uint32 | int[1] | int/long[3] | *uint32 |
uint64 | 使用可变长度编码 | uint64 | long[1] | int/long[3] | *uint64 |
sint32 | 使用可变长度编码。有符号的 int 值。这些比常规 int32 对负数能更有效地编码 | int32 | int | int | *int32 |
sint64 | 使用可变长度编码。有符号的 int 值。这些比常规 int64 对负数能更有效地编码 | int64 | long | int/long[3] | *int64 |
fixed32 | 总是四个字节。如果值通常大于 228,则比 uint32 更有效。 | uint32 | int[1] | int/long[3] | *uint32 |
fixed64 | 总是八个字节。如果值通常大于 256,则比 uint64 更有效。 | uint64 | long[1] | int/long[3] | *uint64 |
sfixed32 | 总是四个字节 | int32 | int | int | *int32 |
sfixed64 | 总是八个字节 | int64 | long | int/long[3] | *int64 |
bool | bool | boolean | bool | *bool | |
string | 字符串必须始终包含 UTF-8 编码或 7 位 ASCII 文本 | string | String | str/unicode[4] | *string |
bytes | 可以包含任意字节序列 | string | ByteString | str | []byte |
字段编号:
0 ~ 536870911(除去 19000 到 19999 之间的数字)
如果你在 .proto 文件中定义 RPC 服务,你应该使用驼峰命名法(首字母大写)命名 RPC 服务以及其中的 RPC 方法:
service FooService {
rpc GetSomething(FooRequest) returns (FooResponse);
}
Maps定义及特性
如果要在数据定义中创建关联映射,protocol buffers 提供了一种方便快捷的语法:
map<key_type, value_type> map_field = N;
...其中 key_type
可以是任何整数或字符串类型(任何标量类型除浮点类型和 bytes
)。请注意,枚举不是有效的 key_type
。value_type
可以是除 map 之外的任何类型。
因此,举个例子,如果要创建项目映射,其中每个 "Project" message 都与字符串键相关联,则可以像下面这样定义它:
map<string, Project> projects = 3;
生成的 map API 目前可用于所有 proto2 支持的语言。你可以在相关的 API 参考 中找到有关所选语言的 map API 的更多信息。
特性
- maps 不支持扩展
- maps 不能是 repeated、optional、required
- map 值的格式排序和 map 迭代排序未定义,因此你不能依赖于特定顺序的 map 项
- 生成 .proto 的文本格式时,maps 按键排序。数字键按数字排序
- 当解析或合并时,如果有重复的 map 键,则使用最后看到的键。从文本格式解析 map 时,如果存在重复键,则解析可能会失败
向后兼容性
map 语法等效于以下内容,因此不支持 map 的 protocol buffers 实现仍可处理你的数据:
message MapFieldEntry {
optional key_type key = 1;
optional value_type value = 2;
}
repeated MapFieldEntry map_field = N;
任何支持 maps 的 protocol buffers 实现都必须生成和接受上述定义所能接受的数据。
导入定义:
如果要用作字段类型的 message 类型已在另一个 .proto 文件中定义,可以通过导入来使用其他 .proto 文件中的定义。
要导入另一个 .proto 的定义,可以在文件顶部添加一个 import 语句:
import "myproject/other_protos.proto";
Packages
你可以将 optional 可选的包说明符添加到 .proto 文件,以防止 protocol message 类型之间的名称冲突。
package foo.bar;
message Open { ... }
然后,你可以在定义 message 类型的字段时使用包说明符:
message Foo {
...
required foo.bar.Open open = 1;
...
}
package 影响生成的代码的方式取决于你所选择的语言:
- 在 C++ 中,生成的类包含在 C++ 命名空间中。例如,Open 将位于命名空间 foo::bar 中。
- 在 Java 中,除非在 .proto 文件中明确提供选项 java_package,否则该包将用作 Java 包
- 在 Python 中,package 指令被忽略,因为 Python 模块是根据它们在文件系统中的位置进行组织的
请注意,即使 package 指令不直接影响生成的代码,但是例如在 Python 中,仍然强烈建议指定 .proto 文件的包,否则可能导致描述符中的命名冲突并使 proto 对于其他语言不方便。
Packages 和名称解析
protocol buffer 语言中的类型名称解析与 C++ 类似:首先搜索最里面的范围,然后搜索下一个范围,依此类推,每个包被认为是其父包的 “内部”。一个领先的 '.'(例如 .foo.bar.Baz)意味着从最外层的范围开始。
protocol buffer 编译器通过解析导入的 .proto 文件来解析所有类型名称。每种语言的代码生成器都知道如何使用相应的语言类型,即使它具有不同的范围和规则。
二、安装及使用
1.电脑上面安装protoc,命令行使用
下载地址 https://github.com/protocolbuffers/protobuf/releases
解压protoc-3.12.3-win64.zip,双击protoc.exe,将/bin路径配入环境变量
在命令行窗口中输入protoc查看是否配置正确
准备AddressBook.proto文件,内容看上文
cmd中执行命令:
protoc.exe --java_out=./outputFile ./AddressBook.proto
其中./outputFile是生成.java文件的目录,而./AddressBook.proto是指定根据哪个./proto文件来生成
2.Idea中以maven的方式使用
这种方式可以本机电脑不安装protoc,是maven编辑的时候现去下载指定版本的protoc
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sid</groupId>
<artifactId>test-serialize</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
</dependencies>
<build>
<defaultGoal>package</defaultGoal>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<!--protobuf 插件默认的 Phase 为 GenerateCode-->
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<configuration>
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<protocArtifact>com.google.protobuf:protoc:3.12.2:exe:${os.detected.classifier}</protocArtifact>
<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>
</configuration>
<executions>
<execution>
<!--把 Compile mojo和 test compile mojo 绑定到 GenerateCode 阶段。
这样,在 GenerateCode 阶段,会执行此插件的两个 mojo。否则,在Maven 默认的 Compile 或 Test 阶段
,不会执行编译动作。-->
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
目录
注意需要把src/main/proto,右击 proto 目录,在『Mark Directory as...』下选择『Sources Root』