Protocol Buffer概述
Protocol Buffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。本文主要是讲解pb3,来看看pb3的新特性
Protocol Buffer3的新特性
pb3相较于pb2支持更多的语言(Go、Ruby、JavaNano等)也变得更加简洁,去掉了一些相对复杂的的语法和特性。
1、在pb3语法中.proto文件首行需要添加syntax = "proto3"
,使用pb3必须要指明这个版本号,不然默然就使用的是pb2,则会报错
2、字段规则移除了“required”,将“optional”改名为“singular”
3、在pb3语法中显式的 “optional” 关键字被禁止,因为字段默认就是可选的;必填字段不再被支持
4、“repeated”字段默认采用packed编码,在pb2语法中需要明确指明[packed=true]来为字段指定比较紧凑的packed编码方式
5、移除default选项。在pb2中使用default选项为某一字段指定默认值。在pb3语法中,字段默认值的字段类型由系统决定。就是说默认值全部是约定好的,而不再提供指定的默认值,在字段呗设置为默认值的时候,该字段不会被序列化。这样很大程度上面节省了空间,提高了效率。但是无法区分某个字段是赋值了默认的值还是根本就没有赋值,在pb2中就会有问题。比如,在更新协议的时候使用 default 选项为某个字段指定了一个与原来不同的默认值,旧代码获取到的该字段的值会与新代码不一样。
6、枚举类型第一个字段值必须是0(pb语法约定)
7、移除了对group字段的支持
8、在pb3中默认修改重复原生字段来支持打包序列化(当前版本中在C++, Java, Python中实现)。用户依然可以通过设置packed为false来禁用打包序列化
9、添加众所周知的类型 protos (any.proto, empty.proto, timestamp.proto, duration.proto 等).用户可以导入并使用这些protos,就像正规的proto文件一样。额外的运行时支持在每个语言中都可用。
10、pb3强制严格 UTF-8 检查。如果字符串字段包含非UTF-8数据则解析将失败。
11、移除扩展(extensions),替代为新的称为 Any 的标准类型
Protocol Buffer3的使用
window环境下protoc的安装及使用
我这里安装的是3.3.0版本的,这里提供github的下载地址,官网需要翻墙,protoc3.3.0
下载完之后进行解压,进入bin目录,将protooc的路径配置到系统环境变量中的path下面
接下来就可以编写proto文件了
syntax = "proto3"; //定义protobuf协议版本,使用proto3的语法
option java_package = "com.zhouym.test";//文件选项,生成的java代码所在的目录
option java_outer_classname = "PersonProBuf";//类名----PersonProBuf.java
message Person {
//proto3版本取消了required修饰以及optional的默认值
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0; //枚举类型的第一个默认值为0,必须是0
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
// map<string,int32> my_field_01 = 5;
// map<string,bool> my_field_02 = 6;
// map<string,float> my_field_03 = 7;
}
接下来编译我们的proto文件
打开cmd命令窗口,执行如下命令
protoc --proto_path G:\code\thriftdemo\src\main\resources --java_out ../src/main/java G:\code\thriftdemo\src\main\resources\hello.proto
命令解释:
protoc:编译命令
–proto_path:后面跟proto文件所在的目录
–java_out:生成的java文件所在的目录
G:\code\thriftdemo\src\main\resources\hello.proto:proto文件所在目录
执行之后便会生成java文件
proto文件就几行代码,在生成的java文件中代码有上千行。下面调用api来测试下
创建maven项目
在pom文件中导入protobuf依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
然后将生成的java文件拷贝到项目中,在test类中进行api的调用进行测试
package com.zhouym.test;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.List;
/**
* 〈序列化和反序列话的代码〉
*
* @author zhouym
* @create 2019/9/16
* @since 1.0.0
*/
public class CodeTest {
public static void main(String[] args) {
//创建builder对象
PersonProBuf.Person.Builder builder = PersonProBuf.Person.newBuilder();
//通过builder对象为字段设置值
builder.setId(1001);//设置id
builder.setName("zhouym");//设置name
builder.setEmail("[email protected]");//设置email
//设置phone
builder.addPhone(PersonProBuf.Person.PhoneNumber.newBuilder().setNumber("12306").setType(PersonProBuf.Person.PhoneType.MOBILE));
builder.addPhone(PersonProBuf.Person.PhoneNumber.newBuilder().setNumber("10086").setType(PersonProBuf.Person.PhoneType.HOME));
//以下执行序列化以及反序列化操作,先将person对象转换成二进制的字节流对象,然后再将二进制的字节流对象反序列化成person对象
//通过build转换成person对象
PersonProBuf.Person person = builder.build();
//将person对象转换成字节流对象
byte[] personinfo = person.toByteArray();
try {
//通过字节流转换成person对象
PersonProBuf.Person persons = PersonProBuf.Person.parseFrom(personinfo);
System.out.println(persons.getName()+"的email为:"+persons.getEmail());
List<PersonProBuf.Person.PhoneNumber> phoneList = persons.getPhoneList();
for (PersonProBuf.Person.PhoneNumber phoneNumber : phoneList) {
System.out.println(phoneNumber.getNumber());
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
执行结果如下