为了一份Mock数据,开启了Protobuf的救赎之路

存储 存储软件
Protobuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。

[[421682]]

一、背景

近期在做一个需求,该需求需要和后端进行交互,为了并行开发,就跟后端产生了如下的对话:

前端:老铁,可以给份mock数据吗?

后端:mock数据太麻烦了,你自己来吧!!!

前端:我怎么知道数据长啥样,如何mock呀!(可怜)

后端:按照约定的接口mock就行,直接给我抛出了一个proto文件

前端:此时已经一脸懵逼状态,proto是个啥?如何根据proto来mock一份数据?后端为什么要用proto,JSON不香吗?为了弥补上自己欠缺的一环,开启了Protobuf的救赎之路。

二、Protobuf是什么?

Protobuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。其目前已经支持的开发语言有多种(C++、Java、Python、Objective-C、C#、JavaNano、JavaScript、Ruby、Go、PHP),详情可参考(https://github.com/52im/protobuf)。其具有如下优缺点:

优点

(1)序列化后体积小,适合网络传输

(2)支持跨平台、多语言

(3)具有较好的升级和兼容性(具有向后兼容的特性,更新数据结构以后,老版本依旧可以兼容)

(4)序列化和反序列化的速度较快

缺点

Protobuf是二进制协议,编码后的数据可读性差

三、Protobuf的结构

Protobuf用法的使用有很多,本次就通过一个例子来看看其基本使用,具体使用可以在网上搜索相关文档进行学习。

  1. syntax = "proto2" 
  2. package transferData; 
  3.  
  4. message transferMessage { 
  5.     required string name = 1; 
  6.     required int32 age = 2; 
  7.     enum SexEnum { 
  8.         Boy = 0; 
  9.         Girl = 1; 
  10.     } 
  11.     optional SexEnum SexEnum = 3; 

1.syntax = "proto2";

该行用于指定语法版本,目前有两个版本proto2和proto3,两个版本不兼容,如果不指定,默认语法是proto2.

2.package transferData;

用于定义该包的包名;

3.message

message是Protobuf中最基本的数据单元,其中可以嵌套message或其它的基础数据类型的成员;

4.属性

message中的每一行就是一个属性,例如required string name = 1,其组成如下所示:

标注 类型 属性名 属性顺序号 [options]
required string name = 1 一些可选项

(1)标注有三种:

required:必选属性;

optional:可选属性;

repeated:重复字段,类似于动态数组;

(2)类型有多种,每种语言不同,例如:int32、int64、int、float、double、string等;

(3)属性名:用于表征该属性的名称;

(4)属性顺序号:protobuf为了提高数据的压缩和可选性等功能定义的,需要按照顺序进行定义,且不允许有重复;

(5)[options]:protobuf提供了一些内置的options可供选择想,可大大提高protobuf的扩展性。

5.enum

定义消息类型时,可能需要某字段值是一些预设值之一,此时枚举类型就能够发挥作用了。

注:protobuf还有很多用法,此处只做了简单介绍,有喜欢的同学可进一步自己深入学习。

四、实战

聊了那么多,下面就进入实战环节,实战将在node运行环境下,构建TCP连接,然后由客户端发送经过Protobuf序列化的内容至服务端,然后服务端接收到信息之后进行解析,其中proto文件的序列化和反序列化将使用protobuf.js包,其是一个纯 JavaScript 实现,支持node.js和浏览器。它易于使用,速度极快,并且可以使用.proto文件开箱即用!(https://www.npmjs.com/package/protobufjs)

4.1 基本使用

本次解析.proto文件使用的是protobuf.js包,常用的方法主要有以下几个:

1.load()

用该函数加载对应的.proto文件,加载完成之后才能够使用里面的message以及进行后续的操作;

2.lookupType()

在加载完.proto后,需要对使用的message进行初始化,即完成message实例化的过程;

3.verify()

该函数用于验证普通对象是某满足对应的message结构;

4.encode()

编码一个message实例或者可利用的普通js对象;

5.decode()

解码buffer至一个message实例,解码失败会排除错误;

6.create()

从一系列属性创建一个新的message实例,其优于通过fromObject创建,是由于其不会产生冗余的转换;

7.fromObject()

将任何无效的普通js对象转换为message实例;

8.toObject()

转换一个message实例去一个任意的普通js对象。

该库的使用还有一些其它方法,可以通过看其对应文档进行学习。对于上述转换关系如下图所示(来自于官方文档):

4.2 服务端

其是服务端,当接收到客户端发送的消息后,利用protobufjs库中的decode函数进行解析,获取解析后的结果。

  1. const net = require('net'); 
  2. const protobuf = require('protobufjs'); 
  3.  
  4. const decodeData = data => { 
  5.     protobuf.load('./transfer.proto'
  6.     .then(root => { 
  7.         const transferMessage = root.lookupType('transferData.transferMessage'); 
  8.  
  9.         const result = transferMessage.decode(data); 
  10.         console.log(result); // transferMessage { name'狍狍', age: 1, sexEnum: 1 } 
  11.     }) 
  12.     .catch(console.log); 
  13. const server = net.createServer(socket => { 
  14.     socket.on('data', data =>{ 
  15.         decodeData(data); 
  16.     }); 
  17.  
  18.     socket.on('close', () => { 
  19.         console.log('client disconnected!!!'); 
  20.     }); 
  21. }); 
  22.  
  23. server.on('error', err => { 
  24.     throw new Error(err); 
  25. }); 
  26.  
  27. server.listen(8081, () => { 
  28.     console.log('server port is 8081'); 
  29. }); 

4.3 客户端

其是客户端对应的代码,利用protobufjs库进行相应的操作,将序列化后的内容发送至服务端。

  1. const net = require('net'); 
  2. const protobuf = require('protobufjs'); 
  3.  
  4. const data = { 
  5.     name'狍狍'
  6.     age: 1, 
  7.     sexEnum: 1 
  8. }; 
  9.  
  10. let client = new net.Socket(); 
  11. client.connect({ 
  12.     port: 8081 
  13. }); 
  14.  
  15. client.on('connect', () => { 
  16.     setMessage(data); 
  17. }); 
  18.  
  19. client.on('data', data => { 
  20.     console.log(data); 
  21.     client.end(); 
  22. }); 
  23.  
  24. function setMessage(data) { 
  25.     protobuf.load('./transfer.proto'
  26.     .then(root =>{ 
  27.         // 根据proto文件中的内容对message进行实例化 
  28.         const transferMessage = root.lookupType('transferData.transferMessage'); 
  29.  
  30.         // 验证 
  31.         const errMsg = transferMessage.verify(data); 
  32.         console.log('errMsg', errMsg); 
  33.         if (errMsg) { 
  34.             throw new Error(errMsg); 
  35.         } 
  36.  
  37.         // 转换为message实例 
  38.         const messageFromObj = transferMessage.fromObject(data); 
  39.         console.log('messageFromObj', messageFromObj); 
  40.  
  41.         // 编码 
  42.         const buffer = transferMessage.encode(messageFromObj).finish(); 
  43.         console.log(buffer); 
  44.  
  45.         // 发送 
  46.         client.write(buffer); 
  47.     }) 
  48.     .catch(console.log); 

 

责任编辑:武晓燕 来源: 前端点线面
相关推荐

2019-04-16 13:57:59

戴尔

2018-01-29 16:29:35

数据开发从业

2019-11-14 21:21:50

数据挖掘数据处理数据分析

2018-08-15 13:49:06

数据分析学习Python

2020-07-15 15:38:15

人脸识别照片活化手机

2020-10-11 21:52:10

数据AI指南

2019-03-24 14:14:40

代码阅读源代码

2023-09-01 14:02:25

用户分析攻略

2015-03-19 15:17:11

2018-07-29 15:33:04

2018-05-03 07:06:21

开发规范iOS

2023-09-29 22:41:26

Kubernetes云原生

2019-12-03 10:28:53

编程语言PythonJava

2020-07-24 09:44:03

安全驻场工程师网络安全IT安全

2016-11-16 13:17:29

大数据人才流动

2017-01-05 18:39:35

数据分析大数据时代分析报告

2019-07-17 07:07:54

MySQL数据库索引

2018-05-22 09:07:54

数据科学语言职位

2018-03-09 10:28:30

生态报告签收

2022-04-29 08:48:25

开源
点赞
收藏

51CTO技术栈公众号