Protocol Buffers 浅析

2,481 阅读3分钟

概念

Protocol Buffers (以下称 PB)是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 不得不说,这个概念看起来就让人一头雾水。其实我们可以通俗的理解为 PB 就是一个类似于 JSON 的数据交换格式,那不同的是 JSON 是文本格式,方便直接阅读,但 PB 是二进制格式,不利于直接阅读。

为什么要使用 PB?

  • 跨语言性,跨平台性

    PB 支持 Java、C++、Python 等多种语言,支持多个平台。前后端文档只需要定义一份描述文件即可协作。

  • 体积小

    PB 对数据序列化后,体积小近 3 倍。

  • 高效

    二进制格式优势

前端需不需要使用 PB?

首先我们来看看前端使用 JSON 的好处: - JS 原生支持,符合 JS 的原生语法,编写阅读成本低。 - 解析方便,使用 JSON.parse()JSON.stringify() 即可,不需要第三方库支持。 - 相较于 PB 不需要描述文件,直接定义传输数据,学习成本低。 - 抓包方便,通过Chrome DevTools等抓包工具抓取 JSON 数据后可以直接查看。 综上所述,JSON 对于 JS 来说是非常契合的数据交换格式,而 PB 需要引入第三方包进行解析,还不利于抓包阅读调试,在什么情况下我们需要使用 PB 呢?

其实主要是单个请求数据比较大的时候,例如几 M 的数据包时,前端主要处理手段是分包拉取,这样的话无论是并发加载还是阻塞加载都会增加代码的复杂性,而且一定程度后都会出现不同的性能瓶颈和请求时间瓶颈。 这个时候就是使用 PB 这种数据交换格式的时候了,相同的数据内容下,大约4.3 M的 PB文件等价于16M 的 JSON 文件。

PB 在前端的使用

PB 在前端中的使用主要分三个阶段:

  1. 编写描述文件,类似于 TS 中申明数据类型
  2. 加载描述文件,编写相应的数据内容
  3. 编译成二进制与反编译

首先npm安装 protobufjs:

npm install protobufjs --save

编写描述文件.proto

// school.classmate.proto, 一般命名规则 ${package_name}.${message_name}.proto
// 自定义 package 名
package school;
// 如果不声明 syntax = "proto3";,则默认使用 proto2 进行解析
syntax = "proto3";

// pb 中的消息载体都被称为 message
// 自定义 message 名
// 定义该 message 中有几个成员,分别的数据类型,是否必要
// 每个消息定义中的每个字段都有唯一的编号。这些字段编号用于标识消息二进制格式中的字段,并且在使用消息类型后不应更改。
// 范围 1 到 15 中的字段编号需要一个字节进行编码,一般用于频繁出现消息
message classmate {
    required int32  id = 1;
    required string name = 2;
    optional int32 sex = 3;
}

编写消息主体文件

// 引入三方库
var Protobuf = require("protobufjs");

// 加载描述文件
Protobuf.load("school.classmate.proto")
.then(function(root) {

	var Message = root.lookupType("school.classmate");
	// 定义消息
	var info = {
		id: 1,
		name: "Jack"
	};
	// 验证是否符合规则
	var errMsg = Message.verify(info);
	if(errMsg)
		throw Error(errMsg);

	var classmateInfo = Message.create(info);
	// classmate { id: 1, name: 'Jack' }

	var buffer = Message.encode(classmateInfo).finish();
	// <Buffer 08 01 12 04 4a 61 63 6b>

	var decodeMessage = Message.decode(buffer);
	// classmate { id: 1, name: 'Jack' }
})