首页 » 网站推广 » phpgoogleprotobuf技巧_谷歌最盛行的序列化格式Protobuf 措辞指南

phpgoogleprotobuf技巧_谷歌最盛行的序列化格式Protobuf 措辞指南

访客 2024-12-13 0

扫一扫用手机浏览

文章目录 [+]

Protobuf中最基本的数据单元是message,是类似Go措辞中构造体的存在。
在message中可以嵌套message或其它的根本数据类型的成员。

教程中将描述如何用protocol buffer措辞布局你的protocol buffer数据,包括.proto文件的语法以及如何通过.proto文件天生数据访问类。
教程中利用的是proto3版本的protocol buffer措辞。

phpgoogleprotobuf技巧_谷歌最盛行的序列化格式Protobuf 措辞指南

定义Message

首先看一个大略的例子,比如说你定义一个搜索要求的message,每一个搜索要求会包含一个搜索的字符串,返回第几页的结果,以及结果集的大小。
在.proto文件中定义如下:

phpgoogleprotobuf技巧_谷歌最盛行的序列化格式Protobuf 措辞指南
(图片来自网络侵删)

.proto文件的第一行指定了利用proto3语法。
如果省略protocol buffer编译器默认利用proto2语法。
他必须是文件中非空非注释行的第一行。
SearchRequest定义中指定了三个字段(name/value键值对),每个字段都会有名称和类型。

指定字段类型

上面的例子中,所有的字段都是标量类型的两个整型(page_number和result_per_page)和一个字符串型(query)。
不过你还可以给字段指定复合类型,包括列举类型和其他message类型

指定字段编号

在message定义中每个字段都有一个唯一的编号,这些编号被用来在二进制体中识别你定义的这些字段,一旦你的message类型被用到后就不应该在修正这些编号了。
把稳在将message编码成二进制体时字段编号1-15将会占用1个字节,16-2047将占用两个字节。
以是在一些频繁利用用的message中,你该当总是先利用前面1-15字段编号。

你可以指定的最

定义字段的规则

message的字段必须符合以下规则:

singular:一个遵照singular规则的字段,在一个构造良好的message体(编码后的message)可以有0或1个该字段(但是不可以有多个)。
这是proto3语法的默认字段规则。
(这个理解起来有些晦涩,举例来说上面例子中三个字段都是singular类型的字段,在编码后的体中可以有0或者1个query字段,但不会有多个。
)repeated:遵照repeated规则的字段在体重可以有任意多个该字段值,这些值的顺序在体重可以保持(便是数组类型的字段)

添加更多类型

在单个.proto文件中可以定义多个message,这在定义多个干系message时非常有用。
比如说,我们定义SearchRequest对应的相应message SearchResponse ,把它加到之前的.proto文件中。

添加注释

.proto文件中的注释和C,C++的注释风格相同,利用// 和 / ... /

/ SearchRequest represents a search query, with pagination options to indicate which results to include in the response. /message SearchRequest {string query = 1;int32 page_number = 2; // Which page number do we want?int32 result_per_page = 3; // Number of results to return per page.}

保留字段

当你删掉或者注释掉message中的一个字段时,未来其他开拓者在更新message定义时就可以重用之前的字段编号。
如果他们意外载入了老版本的.proto文件将会导致严重的问题,比如数据破坏、隐私透露等。
一种避免问题发生的办法是指定保留的字段编号和字段名称。
如果未来有人用了这些字段标识那么在编译时protocol buffer的编译器会报错。

proto会天生什么代码

当利用protocol buffer编译器编译.proto文件时,编译器会根据你在.proto文件中定义的message类型天生指定编程措辞的代码。
天生的代码包括访问和设置字段值、格式化message类型到输出流,从输入流解析出message等。

For C++, the compiler generates a .h and .cc file from each .proto, with a class for each message type described in your file.For Java, the compiler generates a .java file with a class for each message type, as well as a special Builderclasses for creating message class instances.Python is a little different – the Python compiler generates a module with a static descriptor of each message type in your .proto, which is then used with a metaclass to create the necessary Python data access class at runtime.For Go, the compiler generates a .pb.go file with a type for each message type in your file.For Ruby, the compiler generates a .rb file with a Ruby module containing your message types.For Objective-C, the compiler generates a pbobjc.h and pbobjc.m file from each .proto, with a class for each message type described in your file.For C#, the compiler generates a .cs file from each .proto, with a class for each message type described in your file.For Dart, the compiler generates a .pb.dart file with a class for each message type in your file.

标量类型

默认值

当时一个被编码的message体中不存在某个message定义中的singular字段时,在message体解析成的工具中,相应字段会被设置为message定义中该字段的默认值。
默认值依类型而定:

对付字符串,默认值为空字符串。
对付字节,默认值为空字节。
对付bools,默认值为false。
对付数字类型,默认值为零。
对付列举,默认值是第一个定义的列举值,该值必须为0。
对付字段,未设置该字段。
它的确切值取决于措辞。
有关详细信息,请参阅代码天生指南。

列举类型

在定义类型时,您可能希望个中一个字段只有一个预定义的值列表中的值。
例如,假设您要为每个SearchRequest添加corpus字段,个中corpus可以是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO。
您可以非常大略地通过向定义添加列举,并为每个可能的列举值值添加常量来实现。

不才面的例子中,我们添加了一个名为Corpus的列举类型,和一个Corpus类型的字段:

如你所见,Corpus列举的第一个常量映射到了0:所有列举定义都须要包含一个常量映射到0并且作为定义的首行,这是由于:

必须有0值,这样我们就可以将0作为列举的默认值。
proto2语法中首行的列举值总是默认值,为了兼容0值必须作为定义的首行。

利用其他Message类型

可以利用其他message类型作为字段的类型,假设你想在每个SearchResponse中携带类型为Result的,

你可以在同一个.proto文件中定义一个Result类型,然后在SearchResponse中指定一个Result类型的字段。

导入定义

在上面的示例中,Result类型在与SearchResponse相同的文件中定义 - 如果要用作字段类型的类型已在另一个.proto文件中定义,该怎么办?

您可以通过导入来利用其他.proto文件中的定义。
要导入另一个.proto的定义,请在文件顶部添加一个import语句:

import \公众myproject/other_protos.proto\"大众;

默认情形下,您只能利用直接导入的.proto文件中的定义。
但是,有时你可能须要将.proto文件移动到新位置。
现在,你可以在旧位置放置一个虚拟.proto文件,在文件中利用import public语法将所有导入转发到新位置,而不是直接移动.proto文件并在一次变动中更新所有调用点。
任何导入包含import public语句的proto文件的人都可以通报依赖导入公共依赖项。
例如

编译器会在通过命令行参数-I或者--proto-path中指定的文件夹中搜索.proto文件,如果没有供应编译器会在唤其编译器的目录中进行搜索。
常日来说你该当将--proto-path的值设置为你项目的根目录,并对所有导入利用完备限定名称。

利用proto2的类型

可以导入proto2版本的类型到proto3的类型中利用,当然也可以在proto2类型中导入proto3的类型。
但是proto2的列举类型不能直接应用到proto3的语法中。

嵌套类型

类型可以被定义和利用在其他类型中,下面的例子里Result被定义在SearchResponse中

如果你想在外部利用定义在父中的子,利用Parent.Type引用他们

你可以嵌套任意多层

更新Message

如果一个现存的类型不再知足你当前的需求--比如说你希望在中增加一个额外的字段--但是仍想利用由旧版的格式天生的代码,不用担心!
只要记住下面的规则,在更新定义的同时又不毁坏现有的代码就非常大略。

不要变动任何已存字段的字段编号。
如果添加了新字段,任何由旧版格式天生的代码所序列化的,仍能被依据新格式天生的代码所解析。
你该当记住这些元素的默认值这些新天生的代码就能够精确地与由旧代码序列化创建的交互了。
类似的,新代码创建的也能由旧版代码解析:旧版(二进制)在解析时大略地忽略了新增的字段,查看下面的未知字段章节理解更多。
只要在更新后的类型中不再重用字段编号,就可以删除该字段。
你也可以重命名字段,比如说添加OBSOLETE_前缀或者将字段编号设置为reserved,这些未来其他用户就不会意外地重用该字段编号了。

未知字段

未知字段是格式良好的协议缓冲区序列化数据,表示解析器无法识别的字段。
例如,当旧二进制文件解析具有新字段的新二进制文件发送的数据时,这些新字段将成为旧二进制文件中的未知字段。

最初,proto3在解析期间总是丢弃未知字段,但在3.5版本中,我们重新引入了未知字段的保留以匹配proto2行为。
在版本3.5及更高版本中,未知字段在解析期间保留,并包含在序列化输出中。

映射类型

如果你想创建一个映射作为message定义的一部分,protocol buffers供应了一个大略单纯便利的语法

map<key_type, value_type> map_field = N;

key_type可以是任意整数或者字符串(除了浮点数和bytes以外的所有标量类型)。
把稳enum不是一个有效的key_type。
value_type可以是除了映射以外的任意类型(意思是protocol buffers的体中不许可有嵌套map)。

举例来说,如果你想创建一个名为projects的映射,每一个Project关联一个字符串键,你可以像如下来定义:

map<string, Project> projects = 3;映射里的字段不能是follow repeated规则的(意思是映射里字段的值不能是数组)。
映射里的值是无序的,以是不能依赖映射里元素的顺序。
天生.proto的文本格式时,映射按键排序。
数字键按数字排序。
从线路解析或合并时,如果有重复的映射键,则利用末了看到的键。
从文本格式解析映射时,如果存在重复键,则解析可能会失落败。
如果未给映射的字段指定值,字段被序列化时的行为依措辞而定。
在C++, Java和Python中字段类型的默认值会被序列化作为字段值,而其他措辞则不会。

给Message加包名

你可以在.proto文件中添加一个可选的package符来防止类型之前的名称冲突。

在定义message的字段时像如下这样利用package名称

package符对天生代码的影响视编程措辞而定

定义做事

如果想类型与RPC(远程过程调用)系统一起利用,你可以在.proto文件中定义一个RPC做事接口,然后protocol buffer编译器将会根据你选择的编程措辞天生做事接口代码和stub,加入你要定义一个做事,它的一个方法接管SearchRequest返回SearchResponse,你可以在.proto文件中像如下示例这样定义它:

与protocol buffer 一起利用的最大略的RPC系统是gRPC:一种由Google开拓的措辞和平台中立的开源RPC系统。
gRPC特殊适用于protocol buffer,并许可您利用分外的protocol buffer编译器插件直接从.proto文件天生干系的RPC代码。

如果你不想利用gRPC,可以利用自己实现的RPC系统,更多关于实现RPC系统的细节可以在Proto2 Language Guide中找到。

JSON编解码

Proto3支持JSON中的规范编码,使得在系统之间共享数据变得更加随意马虎。
不才表中逐个类型地列出了编码规则。

如果JSON编码数据中短缺某个值,或者其值为null,则在解析为protocol buffer时,它将被阐明为相应的默认值。
如果字段在protocol buffer中具有默认值,则默认情形下将在JSON编码的数据中省略该字段以节省空间。
编写编解码实现可以覆盖这个默认行为在JSON编码的输出中保留具有默认值的字段的选项。

天生代码

要天生Java,Python,C ++,Go,Ruby,Objective-C或C#代码,你须要利用.proto文件中定义的类型,你须要在.proto上运行protocol buffer编译器protoc。
如果尚未安装编译器,请下载该软件包并按照README文件中的解释进行操作。
对付Go,还须要为编译器安装一个分外的代码天生器插件:你可以在GitHub上的golang/protobuf项目中找到这个插件和安装解释。

编译器像下面这样唤起:

IMPORT_PATH指定了在解析import命令时去哪里搜索.proto文件,如果忽略将在当前事情目录进行查找,可以通过通报多次--proto-path参数来指定多个import目录,他们将会按顺序被编译器搜索。
-I=IMPORT_PATH是--proto_path的简短形式。
你可以供应一个或多个输出命令:--cpp_out generates C++ code in DST_DIR. See the C++ generated code referencefor more.--java_out generates Java code in DST_DIR. See the Java generated code referencefor more.--python_out generates Python code in DST_DIR. See the Python generated code reference for more.--go_out generates Go code in DST_DIR. See the Go generated code reference for more.--ruby_out generates Ruby code in DST_DIR. Ruby generated code reference is coming soon!--objc_out generates Objective-C code in DST_DIR. See the Objective-C generated code reference for more.--csharp_out generates C# code in DST_DIR. See the C# generated code referencefor more.--php_out generates PHP code in DST_DIR. See the PHP generated code referencefor more.必须供应一个或多个.proto文件作为输入。
可以一次指定多个.proto文件。
虽然文件是相对付当前目录命名的,但每个文件必须存在于个中一个IMPORT_PATH中,以便编译器可以确定其规范名称。

原文链接:https://segmentfault.com/a/1190000020386857

本文作者:KevinYan,原创授权发布

标签:

相关文章

情商,IT时代的核心竞争力

随着科技的飞速发展,信息技术(IT)已成为推动社会进步的重要力量。在这个充满挑战与机遇的时代,情商(EQ)作为衡量个体综合素质的重...

网站推广 2024-12-15 阅读0 评论0

探寻“isit”,科技与人文的交汇点

“isit”一词,看似简单,实则蕴含着丰富的内涵。在信息爆炸的时代,我们每天都会接触到大量的信息,而“isit”则成为了我们判断信...

网站推广 2024-12-15 阅读0 评论0

扩张性IT,驱动企业数字化转型的新引擎

随着信息技术的飞速发展,企业数字化转型已成为时代潮流。在此背景下,扩张性IT作为一种新兴的IT管理模式,正逐渐成为驱动企业实现数字...

网站推广 2024-12-15 阅读0 评论0