websocket中,使用protobuf的要点总结

作为一个有逼格的实时动态网站,websocket是必不可少的;对于数据量不大的场景,使用json传递数据便绰绰有余了。然后当你想偷懒,直接这么写的时候,老板就会来敲打你的狗头:“现在数据量变多了,网站怎么不动了啊?”

于是,必须使用protobuf了。

protobuf的简介就不多说了,百度都有。

先是本篇中用到的proto定义,具体意义便不用细究了,只是举个例子而已。

syntax = "proto3";
package dsproto;

message EluItem {
    uint32 timestamp = 1;
    string label = 2;
    string name = 3;
    float price = 4;
    float vol = 5;
    float bsv = 6;
    float ssv = 7;
    float highLimit = 8;
    float lowLimit = 9;
}

enum MsgType {
    SINGLE_ELU = 0;
    ALL_ELU = 1;
}

message Msg{
    MsgType type = 1;
    oneof content {
        SingleEluWrapper singleEluWrapper = 2;
        AllEluWrapper allEluWrapper  = 3;
    }
}

message SingleEluWrapper {
    EluItem eluItem = 1;
}

message AllEluWrapper {
    repeated EluItem eluItems = 1;
}

1.后台要点

后台写法没什么多说的。这里以golang为例,用到的protobuf库在此。

首先便是把业务bean转换为protobuf生成的bean,如下:

func (eluItem *EluItem) asProto() *dsproto.EluItem {
	return &dsproto.EluItem{
		Timestamp: eluItem.Timestamp,
		Label: eluItem.Label,
		Name: eluItem.Name,
		Price: eluItem.Price,
		Vol: eluItem.Vol,
		Bsv: eluItem.BSV,
		Ssv: eluItem.SSV,
		HighLimit: eluItem.HighLimit,
		LowLimit: eluItem.LowLimit,
	}
}

然后提供打包成最终需要序列化的消息包的方法:

func packEluItem(eluItem *EluItem) *dsproto.Msg{
	msg := &dsproto.Msg{
		Type: dsproto.MsgType_SINGLE_ELU,
		Content: &dsproto.Msg_SingleEluWrapper{
			SingleEluWrapper: &dsproto.SingleEluWrapper{
				EluItem: eluItem.asProto(),
			},
		},
	}
	return msg
}

这个dsproto.Msg就是我们需要往客户端发送的数据了。例如:

func sendMsg(session *wssession.Session, msg *dsproto.Msg) {
	if data, err := proto.Marshal(msg); err == nil {
		session.Send(data)
	}
}

此时的data已经是序列化好的二进制数据,最后一步便是通过websocket发送出去:

s.socket.WriteMessage(websocket.BinaryMessage, data)

此处一定要是BinaryMessage,而不是TextMessage

2.前端要点

前端用到了protobuf.js这个库。请仔细阅读下面的文档, 并将其安装到当前前端工程。

这里我选择将proto编译成json文件,用到了pbjs这个工具。

所以先npm install pbjs --global安装之。

然后使用pbjs -t json src/proto/dsproto.proto -o src/proto/dsproto.json将其编译为json文件,这里为了方便,可以将此命令写入package.json:

  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "lint": "eslint --ext .js,.vue src",
    "build": "node build/build.js",
    "proto": "pbjs -t json src/proto/dsproto.proto -o src/proto/dsproto.json"
  },

这样每次proto文件变动后,只需要npm run proto即可同步更新。

接下来开始正式撸码:

一个全局helper类是必不可少的,例如:

import dsprotoJson from '@/proto/dsproto.json'
import protobuf from 'protobufjs'

class ProtoHelper {
  constructor () {
    let root = protobuf.Root.fromJSON(dsprotoJson)
    this.Msg = root.lookup('dsproto.Msg')
    this.MsgType = root.lookup('dsproto.MsgType')
  }
}

export default new ProtoHelper()

将需要用到的类型缓存在protoHelper中。

于是,可以在websocket的onmessage方法中愉快的使用protobuf了:

onmessage (e) {
   console.log(protoHelper.Msg.decode(new Uint8Array(e.data)))
}

protoHelper.Msg.decode(new Uint8Array(e.data))得到的便是Msg的实体了,可以发给上层业务逻辑进行处理。

当然,到此还并没有完。最为核心的一个配置,便是将你的websocket做如下设置

ws.binaryType = 'arraybuffer'

 

你可能感兴趣的:(杂项,protobuf,websocket)