Protocol Buffer3是什么?

原创 Lin_Grady 教程 网络协议 75阅读 2018-12-11 17:31:58 举报

Protocol Buffer

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

  • 数据体积小,比xml小3-5倍
  • 二进制格式,数据描述自定义,语义化清晰
  • 性能高效,比xml快20-100倍
  • 跨平台语言,前后端通用
  • 向后兼容性好

局限:

  • 不适合用来对基于文本的标记文档(如 HTML)建模
  • 除非你有 .proto 定义 ,否则无法直接读取数据内容

以一段信息描述为例

xml

经过压缩以后,去掉所有空格正常传输

protocol buffers

通过编码以后,以二进制的方式进行数据传输

示例

命名规则: 包名.消息名.proto

注意:

  • 如果开头第一行不声明 syntax = "proto3";,则默认使用 proto2 进行解析。
  • 语法说明(syntax)前只能是空行或者注释
  • 每个字段由字段限制、字段类型、字段名和编号四部分组成

packageName.MessageName.proto

字段限制

  • required:必须赋值的字段
  • optional:可有可无的字段
  • repeated:可重复字段(变长字段)

由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码

字段类型

.proto TypeNotesNotesC++ TypeJava TypePython Type[2]Go TypeRuby TypeC# TypePHP Type
doubledoubledoublefloatfloat64Floatdoublefloat
floatfloatfloatfloatfloat32Floatfloatfloat
int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
uint32使用变长编码uint32intint/longuint32Fixnum 或者 Bignum(根据需要)uintinteger
uint64使用变长编码uint64longint/longuint64Bignumulonginteger/string
sint32使用变长编码,这些编码在负值时比int32高效的多int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sint64使用变长编码,有符号的整型值。编码时比通常的int64高效。int64longint/longint64Bignumlonginteger/string
fixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。uint32intintuint32Fixnum 或者 Bignum(根据需要)uintinteger
fixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。uint64longint/longuint64Bignumulonginteger/string
sfixed32总是4个字节int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sfixed64总是8个字节int64longint/longint64Bignumlonginteger/string
boolboolbooleanboolboolTrueClass/FalseClassboolboolean
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。stringStringstr/unicodestringString (UTF-8)stringstring
bytes可能包含任意顺序的字节数据。stringByteStringstr[]byteString (ASCII-8BIT)ByteStringstring

标识号

在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。

基本语法

预留字段

如果消息的字段被移除或注释掉,但是使用者可能重复使用字段编码,就有可能导致例如数据损坏、隐私漏洞等问题。一种避免此类问题的方法就是指明这些删除的字段是保留的。如果有用户使用这些字段的编号,protocol buffer编译器会发出告警。

缺省值

如果没有指定默认值,则会使用系统默认值,对于string默认值为空字符串,对于bool默认值为false,对于数值类型默认值为0,对于enum默认值为定义中的第一个元素,对于repeated默认值为空。

枚举

即参数限定只能其中一种

注意:

  • 通过设置可选参数allow_alias为true,就可以在枚举结构中使用别名(两个值元素值相同)
  • 由于枚举值采用varint编码,所以为了提高效率,不建议枚举值取负数。这些枚举值可以在其他消息定义中重复使用。

嵌套类型

可以使用一个消息的定义作为另一个消息的字段类型。

或者

表映射

key_type可以是除浮点指针或bytes外的其他基本类型,value_type可以是任意类型

  • Map的字段不可以是重复的(repeated)
  • 线性顺序和map值的的迭代顺序是未定义的,所以不能期待map的元素是有序的
  • maps可以通过key来排序,数值类型的key通过比较数值进行排序
  • 线性解析或者合并的时候,如果出现重复的key值,最后一个key将被使用。从文本格式来解析map,如果出现重复key值则解析失败。

命名规范

message 采用首字母大写开头驼峰写法。字段名采用下划线分隔命名。

枚举类型全部大写,并且采用下划线分隔命名。

每个枚举值用分号结束,不是逗号

 RPC(远程过程调用)

如果要使用 RPC(远程过程调用)系统的消息类型,可以在 .proto 文件中定义 RPC 服务接口,protocol buffer 编译器将使用所选语言生成服务接口代码和 stubs。

例如,如果你定义一个 RPC 服务GetGameList ,入参是 GameListReq 返回值是 GameListRes ,你可以在你的 .proto 文件中定义它,如下所示:

ProtoBuf.js

pb是一个跨语言,跨平台,可扩展的序列化结构数据格式的方式.用于通讯协议和数据保存等等,最初由谷歌设计.

pb是一个由typescript完成的纯javascript实现,支持nodejs和浏览器,它容易使用,速度高效和.proto文件一起工作.

官网示例

实际项目使用我们不直接解析.proto文件,而是将他们合并打包成成json文件引用.

首先全局安装pb

使用内置的pbjs将.proto合并转成json使用

命令行接口(CLI)可用于在文件格式之间进行转换,并生成静态代码和TypeScript定义。

对于生产环境,建议把你所有的.proto文件打包单个json文件,它最小化了网络请求的数量,并避免了任何解析器的开销(提示:使用光库):

映射和静态区别

SourceLibraryAdvantagesTradeoffs
.protofull易于编辑,与其他库的互操作性,没有编译步骤一些解析和可能的网络开销
JSONlight易于编辑,没有解析开销,单个包(没有网络开销)protobuf.js 特有的一个编译步骤
staticminimal在eval访问被限制, 完整记录,Small footprint for small protos很难编辑,没有反射,有一个编译步骤

大体流程

1, 将.proto文件转义成json;

2, 引入文件构建类型对象;

3, 实例化类型对象序列化成二进制数据发送给服务端;

4, 从服务端拿到二进制数据反序列化解析成类型对象转成js对象使用;

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复