目录
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。通信时所传递的信息是通过Protobuf定义的message数据结构进行打包,然后编译成二进制的码流再进行传输或者存储。
Protocol buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
Protobuf 使用的时候必须写一个 IDL(Interface description language)文件,在里面定义好数据结构,只有预先定义了的数据结构,才能被序列化和反序列化。其中,序列化是将对象转换二进制数据,反序列化是将二进制数据转换成对象。
ProtoBuf 编码格式类似于TLV 格式(Tag | Length | Value),Tag为字段唯一标识,Length为Value域的长度,Value为数据本身。其中,Tag由field_number和wire_type两个部分组成,field_number是message定义字段时指定的字段编号,wire_type是ProtoBuf 编码类型,根据这个类型选择不同的 Value 编码方案。wire_type最多可表达8种编码类型(因为是3bit的),目前已经定义了六种(Varint,64-bit,Length-delimited,Start group,End group,32-bit,其中Start group和End group已经弃用)。
在ProtoBuf 中,Length是可选的,即有些类型的数据结构编码后格式是(Tag | Value),如Varint,64-bit和32-bit。为确定这种格式的数据边界引入了Varint编码。
spring boot + maven
在Pom文件中添加以下配置。
- "1.0" encoding="UTF-8"?>
-
- ...
- <properties>
- ...
- <grpc.version>1.6.1grpc.version>
- <protobuf.version>3.3.0protobuf.version>
- <os.detected.classifer>windows-x86_64os.detected.classifer>
- ...
- properties>
- ...
- <build>
- <plugins>
- <plugin>
- <groupId>org.xolstice.maven.pluginsgroupId>
- <artifactId>protobuf-maven-pluginartifactId>
- <version>0.5.0version>
- <configuration>
- <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.vlassifer}protocArtifact>
- <pluginId>grpc-javapluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifer}pluginArtifact>
- <protoSourceRoot>src/main/resources/protoprotoSourceRoot>
- <outputDorectory>src/main/jsvsoutputDorectory>
- <clearOutputDirectory>false/clearOutputDirectory>
- configuration>
- <executions>
- <execution>
- <goals>
- <goal>compilegoal>
- <goal>compile-customgoal>
- goals>
- execution>
- executions>
- <plugin>
- plugins>
- build>
- ...
- project>
vue
- npm install pbjs
- npm install protobufjs
在package.json中添加一下命令,其中xxx对应每个proto文件的名称,如果有多个文件,需要分多次执行
- {
- "scripts": {
- "proto": "pbjs -t json-module -w commonjs -o --ts src/utils/proto/xxx.ts src/utils/proto/xxx.proto"
- }
- }
student结构
- syntax = "proto3";
-
- package com.test.proto;
-
- option java_multiple_diles = false;
- option java_package = "com.test.protobuf";
- option java_outer_classname = "StudentVO";
-
- message Student {
- int32 number = 1;
- string name = 2;
- }
继承student的class结构
- syntax = "proto3";
-
- import "student.proto";
-
- pacakge com.test.proto;
-
- option java_mutiple_files = false;
- option java_package = "com.test.protobuf";
- option java_outer_classname = "ClassVO";
-
- message Class {
- enum Grade {
- One = 0;
- Two = 1;
- Three = 2;
- };
-
- string name = 1;
- Grade grade = 2;
- com.test.proto.Student student = 3;
- }
rpc服务
- syntax = "proto3";
-
- import "class.proto";
-
- pacakge com.test.proto;
-
- option java_mutiple_files = false;
- option java_package = "com.test.protobuf";
- option java_outer_classname = "ClassService";
-
- service ClassService {
- rpc SendClassMessage(Class) return (Result) {}
- }
-
- message Result {
- enum Statu {
- SUCCESS = 0;
- FAIL = 1;
- }
-
- State result = 1;
- }
要在.proto文件中添加注释,请使用C/ C++风格的//和/… /语法。
- syntax:指定Proto语法,默认使用的是proto2;
- import:用于导入包,类似python;
- package:定义包的名称
- option:用于java。指定输出的是java包名称以及类的名称
- message:数据结构
- enum:枚举类型
- repeated: 表示列表
- service:rpc服务
编译前要确保前后端使用的是同样的proto文件
添加了依赖之后可以发现Maven中会有一个Protobuf的插件,点击插件中的compile就可以编译生成java格式的文件了
修改package.json中的文件名后,执行npm run proto即可
前面已经使用编译工具生成了前后端不同语言的proto类,类中定义了proto的结构以及decode\encode方法,因此使用起来很简单,只要建立普通的前后端通信,如http\websocket\rpc等,然后使用proto类作为信息的载体即可。
以Student为例,前后端通信接口可以类似如下方式建立(代码不完整,删减了部分信息)。
StudentController.java
- @Api(tags = "学生信息接口")
- @RestController
- @RequestMapping("student")
- @CrossOrigin
- public class StudentController {
-
- @Autowired
- private StudentService studentService;
-
- @ApiOperation("根据班级信息获取学生信息")
- @PostMapping(value="queryStudentData", produces="application/x-protobuf")
- public StudentVO.Student queryStudentData(
- @ApiParam(name = "number", value = "学号")
- @RequestParam("number") Integer number
- ) throws IOException {
- return studentService.queryStudentData(number);
- }
- }
StudentService.java
- public interface StudentService {
- StudentVO.Student queryStudentData(Integer number);
- }
StudentServiceImpl.java
- public StudentVO.Student queryStudentData(Integer number) {
- StudentVO.Student.Builder student = StudentVO.Student.newBuilder();
- student.setNumber(number);
- student.setName("student1");
- return student.build();
- }
studentServer.ts
- export async function queryStudentData (name: number) {
- return axios.create(
- {
- timeout: 5 * 60 * 1000,
- baseURL: 'http://' + import.meta.env.IP + ':' + import.meta.env.PORT
- }
- ).post(
- '/student/queryStudentData?number=' + number
- )
- }
student.ts
- import { queryStudentData } from '@/api/http/studentServer.ts'
-
- function getStudent(num: number) {
- let student
- await queryStudentData(1).then((res: any) => {
- let uint8 = new Uint8Array(res.data)
- student = enc.decode(uint8)
- })
- return student
- }