文档章节

protobuf 语法浅析

hgfgoodcreate
 hgfgoodcreate
发布于 2016/07/16 10:40
字数 2157
阅读 372
收藏 0

Protobuf

为什么用Protobuf Buffer

跨语言平台编程,使用SOAP的话,该方式是使用xml的方式传输,会大大增加网络的IO,而且xml的解析复杂,降低报文的解析性能。

定义一个Protobuf消息

message LogonReqMessage{
	required int64 actID=1;
	required string passwd=2;
}

关键说明:

  1. message是消息的关键字
  2. LogonReqMessage是消息名字,相当于java的类名
  3. required前缀表示该字段未必要字段,序列化前后必须赋值的字段。protobuf还存在两个类似的关键字optionalrepeated主要用于表示数组字段。
  4. int64和string表示长整型和字符串类型的消息字段,在protobuf中存在一张类型对照表,即protobuf类型与其他语言的类型对照。
  5. actID和passwd分别表示消息的字段名,等同于java中的域名变量名。
  6. 标签数字12表示不同的字段在序列化前后的二进制中的布局位置。在本例中,passwd字段编码后的数据一定位于actID后,该值在同一个message中不能重复。对于protobuf而言,标签1到15的字段在编码的时候是可以得到优化的(标签值和类型信息只占一个byte,标签范围16到2047占两个byte),protobuf可支持的字段数量是$2^{29}-1$。因此,应该将repeated类型的字段标签未与1~15,节省编码后的字节数量。

定义第二个protobuf消息

enum UserStatus{
	OFFLINE=0;
	ONLINE=1;
}

message UserInfo{
	required int64 actID=1;
	required string name=2;
	required UserStatus=3;
}

关键说明:

  1. enum是枚举类型的关键字,等同于java里面的enum
  2. UserStatus为枚举的名字
  3. 和java一样枚举之间的分隔符是分好;而不是逗号,
  4. OFFLINEONLINE表示枚举值
  5. 01表示枚举所对应的实际整型值,可为任意整型值,无需从0开始。

定义第三个protobuf消息

enum UserStatus{
	OFFLINE=0;
	ONLINE=1;
}

message UserInfo{
	required int64 actID=1;
	required string name=2;
	required UserStatus=3;
}

message LogonRespMessage{
	required LoginResult logonResult = 1;
	required UserInfo userInfo = 2;
}

关键说明:

  1. LogonRespMessage消息定义中包含另外一个消息类型作为其字段,如UserInfo userInfo
  2. 上例的UserInfo和LogonRespMessage被定义在同一个.proto文件中,那么怎么包含其他proto文件中的message呢?ProtoBuf提供关键字import将其他proto文件的message引入到当前proto文件中。例如Import "myproject/CommonMessages.proto"

限定符号

  1. 每个消息必须有一个字段是required类型的字段。
  2. 每个消息包含0个或多个optional类型的字段。
  3. repeated表示的字段可以包含0个或多个数据,有别于java的数组,因为java数组中至少包含一个元素
  4. 如果打算在原有消息协议中添加新的字段,同时保证老的字段能正常的读取写入,那么新添加的字段必须是optional或者repeated类型的。

proto类型对照表

.proto Type|Notes|C++ Type|Java Type -|-|-|- double||double|double float||float|float int32|"Uses variable-length encoding. Inefficient for encoding negative numbers ¨C if your field is likely to have negative values use sint32 instead."|int32|int int64|"Uses variable-length encoding. Inefficient for encoding negative numbers ¨C if your field is likely to have negative values use sint64 instead."|int64|long uint32|Uses variable-length encoding.|uint32|int uint64|Uses variable-length encoding.|uint64|long sint32|Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.|int32|int sint64|Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.|int64|long fixed32|Always four bytes. More efficient than uint32 if values are often greater than 228.|uint32|int fixed64|Always eight bytes. More efficient than uint64 if values are often greater than 256.|uint64|long sfixed32|Always four bytes.|int32|int sfixed64|Always eight bytes.|int64|long bool||bool|boolean string|A string must always contain UTF-8 encoded or 7-bit ASCII text.|string|String bytes|May contain any arbitrary sequence of bytes.|string|ByteString

protobuf消息升级原则

  1. 不要修改已经存在字段的标签号
  2. 任何新添加的字段必须是optional或者repeated限定符,否则无法保证新老程序在互传消息的时候消息的兼容性。
  3. 在原有消息中,不能移除已经存在的required字段,optionalrepeated类型字段可以移除,但是他们之前使用的标签不能使用了。
  4. int32uint32int64uint64bool等类型之间是兼容的,sint32sint64是兼容的,stirngbyte是兼容的,fixed32sfixed32是兼容的,fixed64sfixed64是兼容的,如果想修改原有字段类型,为了保证兼容性,只能将其修改为原有类型兼容的类型,否则打破新老消息格式的兼容性。
  5. optionalrepeated限定符是相互兼容的。

packages

可以在.proto文件中定义包名,如:package abc.lypgone,该包名生成c++时,替换成名字空间,而java为java的包名。

options

protobuf 在.proto文件中定义一些常用的选项,这样protobuf可以帮助我们生成更匹配的目标语言代码。

protobuf的内置选项分为三个等级:

  1. 文件级,这样的选项影响当前文件的所有消息和枚举。
  2. 消息级,这样的选项仅影响某个消息及其包含的所有字段。
  3. 字段级,这样的选项近影响某个字段。

常用的protobuf选项有:

  1. option java_package="com.companyname.projectname"; java_package 是文件级的选项,指定让生成的java代码的包名为该选项的值;与此同时,输出的文件也自动输出到对应包目录下(上例的com/companyname/projectname目录下),该选项对C++没有影响。
  2. option java_outer_classname="LYPhoneMessage"; java_outer_classname是文件级别的选项,指定生成的java代码的外部类名称。如果没有指定,java代码的外部类名称为当前文件的文件名部分,同时将文件名转为驼峰格式,如my_project.proto,那么该文件的外部类名为MyProject,该选项对C++代码无影响。

注意,由于一个java文件只能有一个外部类或者外部接口,所以.proto文件中的message定义的消息均为外部类的内部类,这样才能将这些消息定义到一个文件中。c++没有此限制。

  1. option optimize_for = LITE_RUNTIME; optimize_for 是文件级选项,是protobuf优化选项。该选项又分为3个等级:
    1. SPEED:表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间
    2. CODE_SIZE: 和SPEED恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,通常用于资源有限的平台,如Mobile。
    3. LITE_RUNTIME: 生成的代码执行效率高,同时生成代码编译后的所占用的空间也是非常少。这是以牺牲Protocol Buffer提供的反射功能为代价的。因此我们在C++中链接Protocol Buffer库时仅需链接libprotobuf-lite,而非libprotobuf。在Java中仅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar

对于LITE_MESSAGE选项而言,其生成的代码均将继承自MessageLite,而非Message

  1. [pack = true]: 因为历史原因,对于数值型的repeated字段,如int32、int64等,在编码时并没有得到很好的优化,然而在新近版本的Protocol Buffer中,可通过添加[pack=true]的字段选项,以通知Protocol Buffer在为该类型的消息对象编码时更加高效。如:repeated int32 samples = 4 [packed=true]

注:该选项仅适用于2.3.0以上的Protocol Buffer

  1. [default = default_value]: optional类型的字段,如果在序列化时没有被设置,或者是老版本的消息中根本不存在该字段,那么在反序列化该类型的消息是,optional的字段将被赋予类型相关的缺省值,如bool被设置为false,int32被设置为0。Protocol Buffer也支持自定义的缺省值,如:optional int32 result_per_page = 3 [default = 10]。

命令行编译工具

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

这里将给出上述命令的参数解释。

  1. protoc为Protocol Buffer提供的命令行编译工具
  2. --proto_path等同于-I选项,主要用于指定待编译的.proto消息定义文件所在的目录,该选项可以被同时指定多个。
  3. --cpp_out选项表示生成C++代码--java_out表示生成Java代码--python_out则表示生成Python代码,其后的目录为生成后的代码所存放的目录。
  4. path/to/file.proto表示待编译的消息定义文件

注:对于C++而言,通过Protocol Buffer编译工具,可以将每个.proto文件生成出一对.h和.cc的C++代码文件。生成后的文件可以直接加载到应用程序所在的工程项目中。如:MyMessage.proto生成的文件为MyMessage.pb.h和MyMessage.pb.cc。

© 著作权归作者所有

共有 人打赏支持
hgfgoodcreate
粉丝 8
博文 51
码字总数 117746
作品 0
海淀
程序员
Derek解读Bytom源码-protobuf生成比原核心代码

作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 本章介绍bytom代码Api-Server接口服务 作者使用MacOS操作系统,其他平台...

比原链Bytom
08/23
0
0
grpc| python 实战 grpc

date: 2018-5-15 22:12:32 title: grpc| python 实战 grpc description: 只要代码可以跑起来, 很多难题都会迎刃而解. so, keep coding and stay hungry. 之前用 swoole 写 server 时就接触过...

daydaygo
05/16
0
0
Protobuffer | PHP安装Google protobuf及使用

PHP安装Google protobuf及使用 备注 项目采用redis集群(主从方式)存储数据;数据量月增50W,单个数据序列化情况下达到2k,继续压缩数据解决空间. 项目服务采用PHP(版本5.3)作为RPC服务版本. pro...

云迹
2017/02/05
0
0
Notepad++ 的 protobuf 高亮显示和列表显示

项目地址: https://github.com/chai2010/notepadplus-protobuf

chai2010
2015/07/29
0
0
Google Protobuf 开发指南

Google Protobuf开发指南 1.简介 l 它是开源项目:http://code.google.com/p/protobuf/ l 由google开发,并且在google内部使用 l Protobuf的作用和xml、json是一回事,但他是二进制格式,性能...

macwe
2013/08/29
0
1

没有更多内容

加载失败,请刷新页面

加载更多

sourcetree 离线免注册登录安装教程

Sourcetree是一个优秀的git可视化管理工具,深受开发者喜爱Sourcetree官网,但是在安装时需要谷歌账户登录,需要翻qiang才可以,此一点一直被人们所诟病。今天本教程就为大家提供离线免登陆安...

QQZZFT
17分钟前
1
0
使用 PostgreSQL 解决一个实际的统计分析问题

使用 PostgreSQL 解决一个实际的统计分析问题作者:老农民(刘启华)Email: 46715422@qq.com 之前有个朋友扔给我一个奇葩需求,他们公司之前做了一批问卷调查,全部都是统一格式的excel...

新疆老农民
20分钟前
1
0
TypeScript基础入门之高级类型的映射类型

转发 TypeScript基础入门之高级类型的映射类型 高级类型 映射类型 一个常见的任务是将一个已知的类型每个属性都变为可选的: interface PersonPartial {    name?: string;    age?...

durban
35分钟前
1
0
Dubbo源码分析(6):Dubbo内核实现之基于SPI思想Dubbo内核实现

SPI接口定义 定义了@SPI注解 package com.alibaba.dubbo.common.extension; import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.an......

郑加威
36分钟前
1
0
RxJS的另外四种实现方式(后记)—— 同时实现管道和链式编程

目录 RxJS的另外四种实现方式(序) RxJS的另外四种实现方式(一)——代码最小的库 RxJS的另外四种实现方式(二)——代码最小的库(续) RxJS的另外四种实现方式(三)——性能最高的库 Rx...

一个灰
39分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部