Protobuf介绍以及简单使用
一、什么是Protobuf
公司的项目用到了一门技术,之前也没听说过,了解得知,Protobuf是谷歌出的一门技术,用于数据传输。
我们知道,传统的数据传输格式,有JSON,XML,这些都不陌生,结合JSON 和 XML 来理解,Protobuf和他们是同一类型的事物,它有它的优点。
Protobuf 对比于他们两个,拥有着体量更小,解析速度更快的优势,所以,在 IM 这种通信应用中,非常适合将 Protobuf 作为数据传输格式。
二、对比JSON和XML
三、protobuf的使用
3.1 编写proto文件
钥使用proto,首先需要编写proto文件,这个文件用于定义数据的类型,定义这次数据传输过程中,数据有哪些字段。
后缀名是.proto,如下:
syntax = "proto3"; // PB协议版本
import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any
package com.protobuf; // 包名,其他 proto 在引用此 proto 的时候,就可以使用 test.protobuf.PersonTest 来使用,
// 注意:和下面的 java_package 是两种易混淆概念,同时定义的时候,java_package 具有较高的优先级
option java_package = "com.jet.protobuf"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="UserProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名
option java_multiple_files = true;
message User {
int32 id = 1; // int 类型
string name = 2; // string 类型
Sex sex = 3; // 枚举类型
repeated PhoneNumber phone = 4; // 引用下面定义的 PhoneNumber 类型的 message
map<string, string> tags = 5; // map 类型
repeated google.protobuf.Any details = 6; // 使用 google 的 any 类型
// 定义一个枚举
enum Sex {
DEFAULT = 0;
MALE = 1;
Female = 2;
}
// 定义一个 message
message PhoneNumber {
string number = 1;
PhoneType type = 2;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
}
}
3.2编译
protoc -I=./ --java_out=./ ./user.proto
或
protoc -proto_path=./ --java_out=./ ./user.proto
编译后生成如下文件,这些文件如何使用下文会介绍。
3.3 语法介绍
每个值后面都有序号,从1开始,枚举从0开始。
repeated 表示该字段是可以重复的,这种重复实际上就是一种数组的结构。
java_multiple_files, java_package, 和 java_outer_classname是专门个Java准备的设置。
其中java_multiple_files指编译过后java文件的个数,如果是true,那么将会一个java对象一个类,如果是false,那么定义的java对象将会被包含在同一个文件中。
java_package指定生成的类应该使用的Java包名称。 如果没有明确的指定,则会使用之前定义的package的值。
java_outer_classname选项定义将表示此文件的包装类的类名。 如果没有给java_outer_classname赋值,它将通过将文件名转换为大写驼峰来生成。 例如,默认情况下,“user.proto”将使用"User"作为包装类名称。
3.4变量类型
对于简单类型来说可以使用bool, int32, float, double, 和 string来定义字段的类型。各个语言的数据类型和proto对应关系如下。
四、使用proto
之前不是生成了3个文件吗,使用的demo如下。
public class ProtoTest {
public static void main(String[] args) {
try {
/** Step1:生成 userBuilder 对象 */
// 构造器
User.Builder userBulider = User.newBuilder();
// 构造器 赋值
userBulider.setId(1);
userBulider.setName("zhangsna");
userBulider.setSex(User.Sex.Female);
User.PhoneNumber.Builder phoneNumber = User.PhoneNumber.newBuilder();
phoneNumber.setNumber("13131313311");
phoneNumber.setType(User.PhoneNumber.PhoneType.HOME);
userBulider.addPhone(phoneNumber);
userBulider.putTags("key","value");
userBulider.addDetails(Any.newBuilder().build());
//构造出类
User user = userBulider.build();
/** Step2:序列化和反序列化 */
// 方式一 byte[]:
// 序列化
byte[] bytes = user.toByteArray();
// 反序列化
User userTestResult = User.parseFrom(bytes);
System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", userTestResult.getName(), userTestResult.getSexValue(), userTestResult.getPhone(0).getNumber()));
// 方式二 ByteString:
// 序列化
ByteString byteString = user.toByteString();
System.out.println(byteString.toString());
// 反序列化
User userTestResult2 = User.parseFrom(byteString);
System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", userTestResult2.getName(), userTestResult2.getSexValue(), userTestResult2.getPhone(0).getNumber()));
// 方式三 InputStream
// 粘包,将一个或者多个protobuf 对象字节写入 stream
// 序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
user.writeDelimitedTo(byteArrayOutputStream);
// 反序列化,从 steam 中读取一个或者多个 protobuf 字节对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
User personTestResult3 = User.parseDelimitedFrom(byteArrayInputStream);
System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", personTestResult3.getName(), personTestResult3.getSexValue(), personTestResult3.getPhone(0).getNumber()));
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
五、总结
XML、JSON、Protobuf 都具有数据结构化和数据序列化的能力,但使用方法不同,proto通过proto文件去构造对象。可以将这个对象生成字节数组,或者流,在网络中传输。
- 点赞
- 收藏
- 关注作者
评论(0)