关于前端:为了一份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…)

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);
}

1.如果感觉这篇文章还不错,来个分享、点赞吧,让更多的人也看到

2.欢送关注公众号前端点线面,一起学习“前端百题斩”,开启前端救赎之路。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理