前言
在学习Netty时,发现可以整合ProtoBuf相关的处理器,所以先来学习一下ProtoBuf相关知识。
关于ProtoBuf
ProtoBuf可以看做一个序列化(对象转成字节数组)和反序列化(字节数组转成对象)工具。相比Java本身的序列化,ProtoBuf可以支持跨语言,如使用Java序列化,使用Python来反序列化。相比XML这个文件格式,ProtoBuf序列化生成的数据更小,传输效率更高。
下载编译器
github地址,这里我们下载windows版本的编译器protoc-3.20.0-win64.zip,解压即可用。
定义proto文件
syntax = "proto3";
package tutorial;
option java_package = "com.imooc.sourcecode.java.google.protobuf.test1";
option java_outer_classname = "PersonProto";
message Person {
optional string name = 1;
optional int32 email = 2;
enum PhoneType {
MOBILE = 0;
HOME = 1;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
syntax = "proto3" 表示使用proto3版本,默认使用proto2版本。
optional 表示当前字段可选,非必填。
string name = 1 每个字段需要有一个唯一的号码,必须大于0。
enum 表示枚举类型。
repeated 表示可重复,Java中就是List。
message 可以看做一个Java类,可以嵌套。
Java处理
添加maven依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.19.4</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.19.4</version>
<scope>runtime</scope>
</dependency>
根据proto文件创建Java序列化和反序列化代码
protoc -I=$SRC_DIR --java_out=$DST_DIR person.proto
-I表示proto文件的路径,--java_out表示生成代码的路径,实际命令为
.\protoc.exe -I=D:\java\code_resp\github_resp\source_code\src\main\java\com\imooc\sourcecode\java\google\protobuf\test1 --java_out=D:\java\code_resp\github_resp\source_code\src\main\java person.proto
--java_out值不要包含具体的包路径,proto文件中已经配置了。
将对象序列化到文件中
import com.imooc.sourcecode.java.google.protobuf.test1.PersonProto.Person;
import com.imooc.sourcecode.java.google.protobuf.test1.PersonProto.Person.PhoneNumber;
import com.imooc.sourcecode.java.google.protobuf.test1.PersonProto.Person.PhoneType;
import java.io.FileOutputStream;
import java.io.IOException;
public class Client {
public static void main(String[] args) throws IOException {
PhoneNumber phoneNumber = PhoneNumber.newBuilder()
.setNumber("2")
.setType(PhoneType.HOME)
.build();
Person person = Person.newBuilder()
.setName("lisi")
.setEmail(12)
.addPhones(phoneNumber)
.build();
person.writeTo(new FileOutputStream("D:/testjar/java_serialize_person"));
}
}
使用Builder模式创建Person对象并序列化到文件中。
将Python序列化的文件反序列化为对象
import com.imooc.sourcecode.java.google.protobuf.test1.PersonProto.Person;
import java.io.FileInputStream;
import java.io.IOException;
public class Client2 {
public static void main(String[] args) throws IOException {
Person person = Person.parseFrom(new FileInputStream("D:/testjar/python_serialize_person"));
System.out.println(person);
}
}
读取文件并反序列化为对象。
Python处理
安装依赖
pip install protobuf
PyCharm中可以这样安装
根据proto文件创建Python序列化和反序列化代码
protoc -I=$SRC_DIR --python_out=$DST_DIR person.proto
-I表示proto文件的路径,--python_out表示生成代码的路径,实际命令为
.\protoc.exe -I=D:\java\code_resp\PycharmProjects\test_protobuf\protobuf --python_out=D:\java\code_resp\PycharmProjects\test_protobuf\protobuf person.proto
将Java序列化的文件反序列化为对象
import person_pb2
def test_deserialize():
with open("D:/testjar/java_serialize_person", "r") as rf:
bytes_read = rf.read()
person = person_pb2.Person()
# 需要将字符串转成字节数组
person.ParseFromString(bytes_read.encode())
print(person)
return
if __name__ == "__main__":
test_deserialize()
pass
从文件中读取到的是字符串类型,需要转成字节数组类型,核心为ParseFromString()方法。
将对象序列化到文件中
import person_pb2
def test_serialize():
person = person_pb2.Person()
person.name = "zhangsan"
person.email = 34
phone = person.phones.add()
phone.number = "3"
phone.type = person.PhoneType.MOBILE
with open("D:/testjar/python_serialize_person", "wb") as f:
f.write(person.SerializeToString())
return
if __name__ == "__main__":
test_serialize()
pass
创建一个Person对象并序列化到文件中,核心为SerializeToString()方法。
总结
通过上述测试,我们可以发现,Java序列化的文件通过Python是可以反序列化成功的,Python序列化的文件通过Java也是可以反序列化成功的。更多ProtoBuf相关用法,请查看官方文档。
参考
Protocol Buffers
protobuf(proto3)极简入门(python为例)
深入 ProtoBuf - 简介