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

简介: 为了一份mock数据,开启了Protobuf的救赎之路

一、背景


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


前端:老铁,可以给份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. 优点


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

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

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

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


  1. 缺点


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


三、Protobuf的结构



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

syntax = "proto2"
package transferData;
message transferMessage {
    required string name = 1;
    required int32 age = 2;
    enum SexEnum {
        Boy = 0;
        Girl = 1;
    }
    optional SexEnum SexEnum = 3;
}
  1. syntax = "proto2";


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


  1. package transferData;


用于定义该包的包名;


  1. message


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


  1. 属性


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的扩展性。


  1. 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以及进行后续的操作;


  1. lookupType()


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


  1. verify()


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


  1. encode()


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


  1. decode()


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


  1. create()


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


  1. fromObject()


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


  1. toObject()


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


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


网络异常,图片无法展示
|



4.2 服务端


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


const net = require('net');
const protobuf = require('protobufjs');
const decodeData = data => {
    protobuf.load('./transfer.proto')
    .then(root => {
        const transferMessage = root.lookupType('transferData.transferMessage');
        const result = transferMessage.decode(data);
        console.log(result); // transferMessage { name: '狍狍', age: 1, sexEnum: 1 }
    })
    .catch(console.log);
}
const server = net.createServer(socket => {
    socket.on('data', data =>{
        decodeData(data);
    });
    socket.on('close', () => {
        console.log('client disconnected!!!');
    });
});
server.on('error', err => {
    throw new Error(err);
});
server.listen(8081, () => {
    console.log('server port is 8081');
});

4.3 客户端


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


const net = require('net');
const protobuf = require('protobufjs');
const data = {
    name: '狍狍',
    age: 1,
    sexEnum: 1
};
let client = new net.Socket();
client.connect({
    port: 8081
});
client.on('connect', () => {
    setMessage(data);
});
client.on('data', data => {
    console.log(data);
    client.end();
});
function setMessage(data) {
    protobuf.load('./transfer.proto')
    .then(root =>{
        // 根据proto文件中的内容对message进行实例化
        const transferMessage = root.lookupType('transferData.transferMessage');
        // 验证
        const errMsg = transferMessage.verify(data);
        console.log('errMsg', errMsg);
        if (errMsg) {
            throw new Error(errMsg);
        }
        // 转换为message实例
        const messageFromObj = transferMessage.fromObject(data);
        console.log('messageFromObj', messageFromObj);
        // 编码
        const buffer = transferMessage.encode(messageFromObj).finish();
        console.log(buffer);
        // 发送
        client.write(buffer);
    })
    .catch(console.log);
}


相关文章
WRF模式案例运行初体验--飓风示例全过程记录
本文主要记录一下首次学习WRF并运行官网案例的全过程。
WRF模式案例运行初体验--飓风示例全过程记录
|
2天前
|
数据库
protobuf 设计,避免频繁打包更新
protobuf 设计,避免频繁打包更新
36 0
|
前端开发
前端知识案例1-依赖收集1-问题解决 原
前端知识案例1-依赖收集1-问题解决 原
44 0
前端知识案例1-依赖收集1-问题解决 原
|
XML 消息中间件 JSON
不接受反驳,性能最强,功能最强的Java日志框架
Logback 算是JAVA 里一个老牌的日志框架,从06年开始第一个版本,迭代至今也十几年了。不过logback最近一个稳定版本还停留在 2017 年,好几年都没有更新;logback的兄弟 slf4j 最近一个稳定版也是2017年,有点凉凉的意思。 而且 logback的异步性能实在拉跨,功能简陋,配置又繁琐,远不及Apache 的新一代日志框架 - Log4j 目前来看,Log4j2 就是王者,其他日志框架都不是对手
|
Java 微服务 Spring
SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷(下)
SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷(下)
|
JSON 监控 Java
SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷(上)
SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷(上)
SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷(上)
|
前端开发 数据可视化 JavaScript
有了这个云端Mock功能,你的简历起码提升30分!
我发现一个现象很久了:很多人都喜欢私下自己做一些项目。也就是一些个人的项目,其实我觉得这样挺好处挺多的: • 1、可以在空闲时间提升自己的技术 • 2、这些个人项目可以写在简历上,为自己加分
有了这个云端Mock功能,你的简历起码提升30分!
|
JSON 前端开发 数据格式
#私藏项目实操分享# 【React工作记录十二】前端对接口参数错误如何解决
#私藏项目实操分享# 【React工作记录十二】前端对接口参数错误如何解决
123 0
|
JSON 算法 前端开发
Java常用的几个Json库,性能强势对比!
本篇通过JMH来测试一下Java中几种常见的JSON解析库的性能。每次都在网上看到别人说什么某某库性能是如何如何的好,碾压其他的库。但是百闻不如一见,只有自己亲手测试过的才是最值得相信的。 JSON不管是在Web开发还是服务器开发中是相当常见的数据传输格式,一般情况我们对于JSON解析构造的性能并不需要过于关心,除非是在性能要求比较高的系统。 目前对于Java开源的JSON类库有很多种,下面我们取4个常用的JSON库进行性能测试对比, 同时根据测试结果分析如果根据实际应用场景选择最合适的JSON库。
1699 0
Java常用的几个Json库,性能强势对比!
|
JSON 前端开发 Java
目前为止全网最全的 SpringBoot 参数传递方案
前言 开发这么多年,肯定还有不少小伙伴搞不清各种类型的参数是如何传递的,很多同学都是拿来即用,复制粘贴一把撸,遇到问题还是一脸懵逼。 姿势 学习参数传递的正确姿势,先说怎么做,再说为什么,本质上还是复制粘贴一把撸,问题是你想问不想问为什么! 传递 用户登录 前端代码: var param = {...
4327 0