Why Protobuf#

protobuf它是Google提供的一个技术, 一个类库, 也可以说是一套规范, 学java的人都知道java有自己的序列化机制, 对不同的java程序来说,他们可以使用同一种序列化机制进行数据的传递, 但是java的序列化机制并不适用于其他的语言比如python

如果想让他们共享数据,我们就得定义中数据格式, 比如xml, 通过xml定义出一个对象, 这样java,python都可以解析xml, 但是在网络上传输xml的话是不是有点浪费资源呢? xml中有大量的冗余的标签没有实际的意义还不能去除, 严重影响性能. 导致传输的效率急剧降低

protobuf的出现就是为了迎战这个效率低的问题

什么是protobuf?#

protobuf 全称是: protocol buffers 是一种语言中立的用于序列化结构化数据 ,相对于XML这种格式的数据来说,protobuf极其小, 机器灵活,我们只要定义好数据的格式, 就可以使用代码生成器生成代码,我们只需要使用它生成出来的代码就能实现轻松编写,读取结构化数据, 并且目前Protobuf支持 C++ , C# , Dart , Go , Java , Python多种语言

安装环境#

想使用protobuf的话我们要先去下载两个工具, 第一个就是protobuf的编译器也就是protoc , 我们一会将使用它把我们定义的 .proto 文件编译成java代码, 然后我们直接使用它生成的java代码就ok

下载链接: https://github.com/protocolbuffers/protobuf/releases>

根据同样的系统选择不同的编译器就ok,我用的windows, 所以选择 :protoc-3.11.0-win64.zip

如果我们想用java玩protobuf , 同样得在上面的链接中将protobuf-java-3.11.0.zip 下载到本地

上手使用#

总体思路:

首先我们只要根据需求制定出 .proto 文件中对消息的描述就ok. 因为代码自动生成:

  • 客户端代码生曾策略: stub(装)
  • 服务端代码生曾策略: skeleton(骨架)

序列化encode和反序列decode化也叫做编码和解码:

使用流程:

  1. 定义结果说明文件: 描述接口对象(结构体), 对象成员,接口方法等一系列信息(这是个文本文件独立于任何变成语言)
  2. 通过RPC框架提供的编译器将接口说明文件编译成具体的语言实现
  3. 在客户端和服务端分别引入RPC编译器生成的文件,即可进行RPC远程过程调用

参照项目官方地址https://github.com/protocolbuffers/protobuf/tree/master/java

我们需要添加maven依赖导入运行时环境, 确保我们下面导入的运行时依赖和protoc的版本一致

Copy
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.11.0</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java-util</artifactId> <version>3.11.0</version> </dependency>

一: 编写 .proto 文件#

这个 .proto 文件, 是一个描述性质的文件,我们使用这个文件去描述通信的双方每次发送的数据的格式是怎么样的,就像XML能描述出一个对象出来

下面是 .proto 文件的示例,语法和java 神似

Copy
// proto有两个版本, 2和3, 这里使用的 proto2 syntax = "proto2"; // 以pakcet包名开始, 为了防止命名的冲突 package tutorial; // 如果我们没有显示的指定 java_package 的话, 他就是用上面的packet当成生成的java类的包名 // 显示的指定了 java_package , 最终生成的java代码包的名字就用 java_package 为准 // 即便是 显示的提供了 java_package, 也得提供上面的packet(防止在其他语言中出现命名的冲突) option java_package, = "com.example.tutorial"; // 最终经过protoc处理后 会生成一个 叫AddressBookProtos的外部类, 这个类中包含了我们指定的下面的所有类 // 如果我们没有显示的指定的话,最终就会将文件名转换为驼峰命名法得到的名,当成类名字 option java_outer_classname = "AddressBookProtos"; // message其实是不同类型的 field的聚合,比如 string, int32,bool,float,double // 第一个消息 message Person { // 这种等于1, 等于2, 并不是赋值, 而是进行一种唯一的标记,在同一个范围中 ,标记不重复 // 如下面的123, 跳过枚举后的4 // required 表示这个字段是必须要有的,如果不提供的话会抛出异常 required string name = 1; required int32 id = 2; // 表示这个字段的值是可选的 optional string email = 3; // 枚举类型的消息 enum PhoneType { MOBILE =