TTprotobuf是一种跨语言的数据转换协议,由google开源的,已支持大部份语言。在一般的数据交互过程中都是使用json,xml等来做数据的转换,这其中涉及复杂的解析与序列化反序列化问题,如果在大量数据并发请求时,也会导致性能问题。protobuf采用Varint编码技术,在某种程度上减少数字的字节数,关于Varint的实现原理可以参考:
https://segmentfault.com/a/1190000020500985?utm_source=tag-newest
由于protobuf是跨语言的,所以用不同的语言序列化对象后,生成一段字节码,之后可以其他任何语言反序列化并自用,大大方便了跨语言的通讯,同时也提高了效率。
本文主要是Protobuf针对java在后台开发的应用,用一个简单的实例工程来实践。
在开始项目之前,先安装protobuf插件,方便后面编写proto文件
- 新建一个maven工程,在项目main下新建目录,命名为proto
- 并导入以下pom依赖(本项目使用protobuf版本为3.5.1):
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.grpc/grpc-all -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 需要先自行配置好maven
- 新建一个包:com.study.myprotobuf.dto:
- 新建两个proto文件:student.proto,course.proto
- 编写proto文件之前,可以先大概百度了解一下proto的语法,实际也跟java或c的类似,有一个头部定义包,文件名,引入其他proto文件等。在这里说明一下,proto文件实际不是业务端要用到的,而是一个模板文件,google开源支持的语言都可以使用这个proto文件。
- 以下是两个proto文件的内容,课程模板与学生模板文件:
// 指定模板类的包路径
option java_package = "com.study.myprotobuf.dto";
// 指定模板类的名称,名称必须是有实际业务意义的
option java_outer_classname = "CourseProto";
// 定义课程
message course{
// 课程名称
required string name = 1;
// 课程分数
required int32 score = 2;
}
// 指定模板类的包路径
option java_package = "com.study.myprotobuf.dto";
// 指定模板类的名称,名称必须是有实际业务意义的
option java_outer_classname = "StudentProto";
// 引入课程模板
import "course.proto";
// 定义学生模板
message student{
// 学生名称
required string name = 1;
// 学生年龄
required int32 age = 2;
// 学生所修课程,可能是多门,是一组数组
repeated course course = 3;
}
- 如果maven配置成功,可以打开maven的Plugins下的protobuf节点中的compile,双击进行编译:
- 编译成功后,生成target目录,找到对应的proto类,如下所示:
-
以上生成java代码类方式也可以通过命令的方式:
从github下载protoc工具:
https://github.com/protocolbuffers/protobuf/releases/tag/v3.5.1
添加环境变量:
打开cmd命令窗口,输入protoc --version,查看版本号:
在本地磁盘新建存放proto文件和生成java类的文件目录:
D:/protofile(proto文件)
D:/protojava(生成的java类)
在cmd命令窗口输入以下命令:
protoc -I=D:/protojava --java_out=D:/protojava D:/protofile/teacher.proto --proto_path=D:/protofile
执行成功后可以D:/protofile目录中生成protobuf的java类文件。
- 将这两个java类复制到之前新建的dto包中,此时,可以看到google生成的对象proto的源代码,里面实际包含了模板类的创建,序列化,反序列化和json转换等方法
- 新建一个test包,新增一个类TestMyProtobuf进行测试,完成的代码如下:
package com.study.myprotobuf.test;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.study.myprotobuf.dto.CourseProto;
import com.study.myprotobuf.dto.StudentProto;
import java.util.Arrays;
public class TestMyProtobuf {
public static void main(String[] args) {
// 生成课程1对象
CourseProto.course.Builder courseBuilder1 = CourseProto.course.newBuilder();
courseBuilder1.setName("Java");
courseBuilder1.setScore(99);
CourseProto.course course1 = courseBuilder1.build();
// 生成课程2对象
CourseProto.course.Builder courseBuilder2 = CourseProto.course.newBuilder();
courseBuilder2.setName("Python");
courseBuilder2.setScore(98);
CourseProto.course course2 = courseBuilder2.build();
// 生成学生对象
StudentProto.student.Builder studentBuilder = StudentProto.student.newBuilder();
studentBuilder.setName("Lucy");
studentBuilder.setAge(23);
studentBuilder.addCourse(0,course1);
studentBuilder.addCourse(1,course2);
StudentProto.student student = studentBuilder.build();
// proto对象
System.out.println("The student object is: \n" + student);
// 序列化
byte[] studentByte = student.toByteArray();
System.out.println("The student after encode is:\n" + Arrays.toString(studentByte));
try {
// 反序列化
StudentProto.student newStudent = StudentProto.student.parseFrom(studentByte);
System.out.println("The student after decode is:\n" + newStudent);
// 转换json
System.out.println("The student json format is:\n" + JsonFormat.printer().print(student));
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
运行结果如下:
The student object is:
name: "Lucy"
age: 23
course {
name: "Java"
score: 99
}
course {
name: "Python"
score: 98
}
The student after encode is:
[10, 4, 76, 117, 99, 121, 16, 23, 26, 8, 10, 4, 74, 97, 118, 97, 16, 99, 26, 10, 10, 6, 80, 121, 116, 104, 111, 110, 16, 98]
The student after decode is:
name: "Lucy"
age: 23
course {
name: "Java"
score: 99
}
course {
name: "Python"
score: 98
}
The student json format is:
{
"name": "Lucy",
"age": 23,
"course": [{
"name": "Java",
"score": 99
}, {
"name": "Python",
"score": 98
}]
}
Process finished with exit code 0
至此,proto基本的使用就已经结束了,实际也是将对象通过模板的方式去定义,以便于通过不同的语言去调用,并且从反序列化的过程中获取。
以上为个在实践过程中的一些操作,如有错误之处,请指出,欢迎讨论,共同进步!谢谢!