调研报告之Protobuf数据格式

标签: 无 分类: 未分类 创建时间:2022-03-31 03:28:25 更新时间:2023-10-20 11:23:28

1.前言

如果要问你,前后端交互的数据格式有哪些?哪种数据格式更加通用?哪种数据格式更加的高效?有没有二级制数据格式?

参考文章:
1.HTTP传输二进制初探 从第一次接触http协议的时候,不知是怎么回事,形成了这么一个错误的观点,认为http协议是个纯ASCII字符协议,也就是说在http流里是看不到二进制流的0x00值的。其实答案是:http协议里的content可以是纯二进制流。
2.protobuf为什么那么快 首先,对于我们系统当中的一些大数据传输,显然用protobuf是可以获得很大的改善的,如果你这么干了,领导一定会想给你涨工资的。第二,给我们优化数据传输提供了一种思路,通过提供更多的数据元数据(数据类型,长度等),我们可以大幅度提高解析数据,比如在nodejs当中就有一个框架叫fastify,通过给json设计了schema来提供更快的解析速度,

作者:张柳哥
链接:https://www.jianshu.com/p/72108f0aefca
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.Protobuf格式

Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。Protocol buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

参考文章:
1.Protobuf 有没有比 JSON 快 5 倍?
2.[转]Protobuf3 语法指南 Protobuf3的语法说明
3.GitHub 标星 6.2k+!前 Google 工程师出品,最佳开发工具大全! 1.大数据处理工具:MapReduce,替代品:Apache Hadoop、Spark。1.序列化工具:Google 内部:Protocol Buffer;外部替代品:Protobuf、Thrift、Avro。3.大型集群管理系统:Google 内部:Borg;外部替代品:Kubernetes、Apache Mesos、HashiCorp Nomad。4.其他开发工具。
4.高效的数据压缩编码方式 Protobuf proto3 定义 message;proto3 定义 Services;Protocol Buffer 编码原理;Protocol Buffer 命名规范
5.长文图解Google的protobuf思考、设计、应用

2.Pbf格式

这个格式,其实是mapbox实现的一种 Protobuf Buffer 的读取,可以用在浏览器端,典型的例子就是早期的矢量切片数据就是以.pbf格式传输的,可以参考 调研报告之矢量切片

3.解析

不同语言都实现了protobuf文件的序列化和返序列化。

参考文章:
1.protobufjs protobuf.js是一个纯JavaScript实现,支持Node.js和浏览器的TypeScript,它容易使用速度快速,可以直接反射.proto文件,不需要生成任何文件。
2.浏览器中使用Protobuf 总体来说,在前端使用 protobuf 并不是太成熟,很可能会遇到一些问题,可以在 Protobuf 的 Github issues 页面搜寻一下相关解决方案或提出你的问题。
3.前后端数据交互利器–Protobuf Protobuf 是 Google 开源的一款用于处理前后端数据交互格式的工具。通常来讲前后端使用的编程语言是不同的,使用 Protobuf无需多虑,前后端只管约定通信协议,之后就可以使用 pb 工具生成代码。

2.安装

我这里没有用windows系统,用的是Deepin系统,从官网 下载安装包 protobuf-all-3.20.0.tar.gz ,使用命令行执行编译安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
## 安装依赖包,如果没有安装的话
sudo apt-get install autoconf automake libtool curl make g++ unzip
## 编译,并指定安装位置
./configure --prefix=/soft/protobuf
make
make check
## 执行安装
sudo make install
sudo ldconfig

## 配置环境变量
vi /etc/profile
## 添加下面两行脚本
export PROTOC_HOME=/soft/protobuf
export PATH=$PROTOC_HOME/bin:$PATH
## 重载环境变量
source /etc/profile
## 查看
protoc --version

3.定义消息类型

编写模式文件

1
2
3
4
5
6
7
8
9
syntax = "proto3";

option java_package = "com.example.demo.protobuf";
option java_outer_classname = "MessageUser";

message MessageUserLogin {
string access_token = 1;
string username = 2;
}

使用protoc生成类文件,执行命令之后,会在当前文件夹生成一个com文件夹,就是我们常用的java类的文件夹了。

1
protoc --java_out=./ user.proto

4.Java端

废话不多说,这里我测试一个简单的例子。后端使用 java 生成 protobuf 格式数据,传递给前端,前端进行解析并打印。我这里直接到springboot官网,生成一个基本的springboot工程,然后通过添加依赖,添加生成的代码,然后编写控制器接口的方法进行工程的创建。

4.1.添加依赖

在pom.xml中添加依赖,因为我安装的protoc是3.20版本的,所以protobuf-java依赖也要用3.20版本的。

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.20.0-rc-1</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.4</version>
</dependency>

4.2.添加protobuf序列化支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

@Configuration
public class CommonConfig {
/**
* protobuf 序列化
*/
@Bean
ProtobufHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufHttpMessageConverter();
}

/**
* protobuf 反序列化
*/
@Bean
RestTemplate restTemplate(ProtobufHttpMessageConverter protobufHttpMessageConverter) {
return new RestTemplate(Collections.singletonList(protobufHttpMessageConverter));
}
}

4.3.添加控制器

将使用protoc工具生成的java端代码,拷贝到工程的相应目录中,并解决依赖等问题,编写控制器,输出数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.demo.controller;

import com.example.demo.protobuf.MessageUser;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
public class DemoController {

@RequestMapping(value = "/test", produces = "application/x-protobuf")
@ResponseBody
public MessageUser.MessageUserLogin getProto() {
MessageUser.MessageUserLogin.Builder builder = MessageUser.MessageUserLogin.newBuilder();
builder.setAccessToken(UUID.randomUUID().toString()+"_res");
builder.setUsername("abc");
return builder.build();
}

}

4.4.启动测试

使用 GET 访问 http://127.0.0.1:8080/test,查看结果:(8d4ac2c5-0a69-4811-a16e-ebd6769eb69f_resabc

5.Javascript端

使用起来还是有点费劲的,需要先生成commonjs模块,然后使用browserify进行编译,最后集成到html中。

参考文章:
1.基于http协议使用protobuf进行前后端交互 这是一个前后端分离的使用proto进行交互的例子,这里有使用axios进行后台数据请求
2.Convert a JSON Object to Buffer and Buffer to JSON Object back
3.Browserify:浏览器加载Node.js模块
4.Protobuf之proto js生成 这个对于使用browerify生成最终的js,有描述

5.1生成js文件

使用protoc工具,根据user.proto配置,生成js文件,会在当前 . 目录下生成一个user_pb.js文件,用于 nodejs 端加载和显示,如果要想进一步的在浏览器中进行加载和展示,还需要通过browserify工具进行打包

1
2
## 生成commonjs模块的文件
protoc --js_out=import_style=commonjs,binary:. user.proto

5.2.编译打包

1
2
3
4
5
6
7
8
9
10
11
12
13
## 安装需要的依赖
npm install -g require
npm install -g browserify
npm install google-protobuf

## 或者
pnpm add -g require
npm add -g browserify
## 当前目录安装
npm add google-protobuf

## 执行编译
browserify user_pb.js > user.js

5.3.编写html

将生成的user.js嵌入到html中,使用axios进行数据请求,然后进行数据解析,在接收的时候,使用二级制进行数据接收和反序列化就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试protobuf</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="js/user.js"></script>
</head>
<body>
<script type="application/javascript">
/**
* 前端使用
*/
console.log(axios);
var message= new proto.MessageUserLogin();
message.setAccessToken(1);
message.setUsername(2);
var bytes = message.serializeBinary();
console.log(bytes);

// 请求后台数据,并序列话
axios({
method:'get',
url: '/test',
headers: { 'contentType':'application/x-protobuf'},
responseType:'arraybuffer' // 要使用二级制数据接收
}).then(res => {
// 二级制解析
var data = new Uint8Array(res.data);
let result = proto.MessageUserLogin.deserializeBinary(data)
let username = result.getUsername()
console.log(username) // username。解析出来就是后端传过来的abc

let pm = proto.MessageUserLogin.deserializeBinary(data)
let protoBuf = pm.toObject()
console.log('protoBuf: ', protoBuf) // 打印出来是一个对象
}).catch((error) => {
console.log(error)
})
</script>
</body>
</html>
参考文章:
1.axios使用protobuf进行通讯 这里我得到了启发,就是使用 arraybuffer 进行数据的接收
2.ProtoBuf: ByteString和String转换
3.httpclient +protobuf 实现数据传输 content-type 设置为 application/octet-stream,后端使用的是通过httpclient的post请求传输protobuf二进制流

6.总结

(1) 当使用字符串类型的时候,不会有多么大的压缩,会被浏览器原生体现,如果使用了数字类型,那么就是二级制类型。

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。