Protocol Buffer是Google提供的一种数据序列化协议,是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

指定版本

protocol 语言文件后面名为.proto

文件第一行指定版本。必须在文件首行指定,之前不能有任何空行和注释。可以不指定,默认为proto2。

  1. syntax = "proto3";

定义Message

以message关键字开头,然后指定名称。消息体中时字段的定义,分别指定类型、名称和字段编号。

  1. message SearchRequest {
  2. string query = 1;
  3. int32 page_number = 2;
  4. int32 result_per_page = 3;
  5. }

示例中只展示了基础类型字段定义,同样可以指定其他枚举类型或者定义好的Message类型。

在二进制格式中,字段编号与类型标识符结合使用。 1到15范围内的字段编号需要一个字节来编码。 从 16 到 2,047 的数字需要 2 个字节。 所以字段编号 1 到 15 的单字节标识符提供更好的性能,所以应将其用于最基本、最常用的字段。

protocol 基础类型与其他语言类型对应关系 Scalar Value Types

关于字段编号是1到2的29次方减一之间的数。不能使用19000到19999之间的数 (FieldDescriptor::kFirstReservedNumber through FieldDescriptor::kLastReservedNumber))。

复杂类型字段声明

  1. message SearchResponse {
  2. repeated Result results = 1;
  3. }
  4. message Result {
  5. string url = 1;
  6. string title = 2;
  7. repeated string snippets = 3;
  8. }

repeated关键字表面该字段是一个重复的值,生成对应语言代码时,会是一个集合字段或属性。

保留字段

如果更新服务的消息删除某些字段,应保证不应重复使用该字段编号。如从现有的message中删除字段应该保留其编号。

  1. message Foo {
  2. reserved 2, 15, 9 to 11; //to表示一个连续的范围值
  3. reserved "foo", "bar";
  4. int32 id = 1;
  5. string name = 3;
  6. }

也可以将reserved 关键字用作未来可能添加字段的占位符。

可以通过编号和名称的方式保留字段,但是不能混合使用

添加注释

protocol 和很多主流语言注释方式相同,使用 ///* ... */的注释方式。

默认值

对于声明的message编码过后,其中定义的字段,会有一个默认的零值。如:

  • string:empty
  • byte:empty bytes
  • bool:false
  • numeric type:0
  • enum:枚举中定义的第一个值,且必须是0
  • For message fields, the field is not set. Its exact value is language-dependent. See the generated code guide for details.

枚举声明

通过enum关键字定义枚举,并什么可以有哪些值。

  1. enum Corpus {
  2. UNIVERSAL = 0;
  3. WEB = 1;
  4. IMAGES = 2;
  5. LOCAL = 3;
  6. NEWS = 4;
  7. PRODUCTS = 5;
  8. VIDEO = 6;
  9. }

可以看到枚举内的第一个常量定义为0,这是必须的,proto3中所有的字段都是必须的(proto3移除了proto2中required和optional的声明),需要定义一个0的常量作为默认值。

如果枚举不需要共用,可以直接在message内声明并定义,如:

  1. message SearchRequest {
  2. string query = 1;
  3. int32 page_number = 2;
  4. int32 result_per_page = 3;
  5. enum Corpus {
  6. UNIVERSAL = 0;
  7. WEB = 1;
  8. IMAGES = 2;
  9. LOCAL = 3;
  10. NEWS = 4;
  11. PRODUCTS = 5;
  12. VIDEO = 6;
  13. }
  14. Corpus corpus = 4;
  15. }

allow_alias

如果需要一个枚举内不同的变量声明相同的值,需要开启allow_alias 选项。

  1. enum EnumAllowingAlias {
  2. option allow_alias = true;
  3. UNKNOWN = 0;
  4. STARTED = 1;
  5. RUNNING = 1;
  6. }

保留值

  1. enum Foo {
  2. reserved 2, 15, 9 to 11, 40 to max;
  3. reserved "FOO", "BAR";
  4. }

和message定义保留字段一样,不支持序号和名称混合使用。

包(命名空间)声明

通过package关键字指定包名,方便工程化管理,避免命名冲突。对应Go中包名,C#的命名空间。

  1. package foo.bar;

包引入

import引入其他proto文件,对应Go的import,C#的using。

  1. import "google/api/annotations.proto";

proto3 和 proto2 不同版本间定义的message类型可以相互引用,但是proto2 定义的枚举不能被proto3 引入使用。

import public

默认情况下,您只能使用直接导入的 .proto文件定义。然而,有时需要移动 .proto文件到一个新的位置,但不想为此更新所有引用它的.proto文件,此时可以在文件原始位置放置一个仿造的 .proto文件,使用import public将所有导入转发到新位置。任何包含import public语句的proto文件都可以临时依赖import public依赖。例如:

  1. // new.proto
  2. // All definitions are moved here
  1. // old.proto
  2. // This is the proto that all clients are importing.
  3. import public "new.proto";
  4. import "other.proto";
  1. // client.proto
  2. import "old.proto";
  3. // You use definitions from old.proto and new.proto, but not other.proto

编译器通过-I/--proto_path参数指定搜索导入的文件的目录。如果没有指定,默认会在调用编译器的目录中查找。通常,您应该将--proto_path标志设置为项目的根目录,并对所有导入使用完全限定的名称。

嵌套类型

可以在一个message内部定义一个message,类似C#、java中的内部类

  1. message SearchResponse {
  2. message Result {
  3. string url = 1;
  4. string title = 2;
  5. repeated string snippets = 3;
  6. }
  7. repeated Result results = 1;
  8. }

通过_Parent_._Type_的形式,在外部重复使用定义的嵌套类型

  1. message SomeOtherMessage {
  2. SearchResponse.Result result = 1;
  3. }

更新消息

如果有对现有message跟新的需求,例如在不破坏现有代码的前提下新增字段。应遵循如下规则:

  • 不要更改现有字段的编号。
  • 如果新增消息字段应提供合理的默认值以保证旧服务的代码与新生成的服务代码之间能正常交互。
  • 如果确保一个消息字段不会再使用可以,可以删除。或者重命名字段怎加OBSOLETE_前缀进行标识,也可以通过预留字段保留字段编号,确保不会重复使用该编号。
  • int32, uint32, int64, uint64和bool类型之间时相互兼容的,意味着可以直接修改相应的字段类型而且不破坏兼容性。应该注意的是类似int64改为int32类型是,超出的数据部分会被截断。
  • sint32 和sint64 彼此兼容,但与其他整数类型不兼容。
  • 如果字节包含消息的编码版本,则嵌入消息与字节兼容。
  • 只要字节是有效的UTF-8类型,字符串和字节之间也是是兼容的。
  • enum与int32、uint32、int64和uint64协议格式兼容(如果这些值不匹配,它们将被截断)。然而,需要注意的是,当消息被反序列化时,客户端代码可能会以不同的方式对待它们:如,无法识别的proto3枚举类型将保留在消息中,但是当消息被反序列化时,如何表示取决于不同的语言。int字段总是保留其值。
  • fixed32与sfixed32兼容, fixed64与sfixed64兼容。
  • 单个字段修改为新的oneof成员也是允许的,如果能明确多个字段没有被同时设置,那么多个字段修改为新的onof成员也是相对安全的。任何字段移入现有的oneof成员都是不安全的。

未知字段

未知字段是格式良好的协议缓冲区序列化数据,用于表示解析器无法识别的字段。proto3早期版本中会丢弃未知字段。3.5之后的版本会在解析期间保留未知字段,并包含在序列化输出中以兼容proto2。

Any 类型

Any类型允许将消息作为嵌入类型使用不需要在proto内定义。 类型 Any 可以表示任何已知的 Protobuf 消息类型。使用Any类型需要引入google/protobuf/any.proto包。

  1. import "google/protobuf/any.proto";
  2. message ErrorStatus {
  3. string message = 1;
  4. repeated google.protobuf.Any details = 2;
  5. }

OneOf

如果消息中包含多个字段,但是最多同时只会设置一个值,可以借助Oneof强制约束并节省内存(oneof集中所有的字段共享内存)。

  1. message SampleMessage {
  2. oneof test_oneof {
  3. string name = 4;
  4. SubMessage sub_message = 9;
  5. }
  6. }

设置oneof字段将自动清除oneof字段的其他所有成员。如果设置了几个oneof字段,只有最后一个字段仍然有值。

  1. SampleMessage message;
  2. message.set_name("name");
  3. CHECK(message.has_name());
  4. message.mutable_sub_message(); // Will clear name field.
  5. CHECK(!message.has_name());

oneof 集内的字段必须具有唯一的字段编号。oneof中可以添加任何类型的字段但是不能使用repeated字段。但是可以在repeated字段类型内使用oneof关键字。

Maps

map关键字可以很方便的声明一个map类型字段:

  1. //map<key_type, value_type> map_field = N;
  2. map<string, Project> projects = 3;

key_type可以是任何整数或字符串类型(除float和bytes外的任何标量类型)。

声明map时不能和repeated一起使用,可以通过如下方式变相定义一个重复的map

  1. message Order {
  2. message Attributes {
  3. map<string, string> values = 1;
  4. }
  5. repeated Attributes attributes = 1;
  6. }

或者

  1. message MapFieldEntry {
  2. key_type key = 1;
  3. value_type value = 2;
  4. }
  5. repeated MapFieldEntry map_field = N;

服务定义

定义Rpc约束,即声明传入和返回值,可以理解为其他语言中接口(抽象)的定义。

  1. service BlogService {
  2. rpc CreateArticle (CreateArticleRequest) returns (CreateArticleReply) {
  3. option (google.api.http) = {
  4. post: "/v1/article/"
  5. body: "*"
  6. };
  7. }
  8. }

Options

选项(Options)不会影响整体声明,改变的时编译时的处理方式,在google/protobuf/descriptor.proto查看完整支持的options。

Options分为文件级(只能声明在文件最顶级)、消息级(什么在message内)、字段级(声明field)。

选项也可以声明在enum 、service等类型上。官网文档原话Options can also be written on enum types, enum values, oneof fields, service types, and service methods; however, no useful options currently exist for any of these.刚接触还不是很理解后面这段话的含义。

如果有需要自定义选项,参考文档

protocol 协议语言介绍的更多相关文章

  1. SIP (Session Initiation Protocol) 协议

    Session Initiation Protocol 介绍 SIP是VoIP技术最常使用的协议,它是一种应用程序层协议,可与其他应用程序层协议配合使用,以控制Internet上的多媒体通信会话. V ...

  2. Arduino语言介绍

    Arduino语言介绍 Arduino语言是建立在C/C++基础上的,其基础是C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化,不用我们去了解他的底层,让不了解AV ...

  3. DHCP (Dynamic Host Configuration Protocol )协议的探讨与分析

    DHCP (Dynamic Host Configuration Protocol )协议的探讨与分析 问题背景 最近在工作中遇到了连接外网的交换机在IPv6地址条件下从运营商自动获取的DNS地址与本 ...

  4. Objective-C( protocol协议)

    protocol 协议 protocol:用来声明方法 1.协议的定义 @protocol 协议名称 <NSObject> // 方法声明列表.... @end 2.如何遵守协议 1> ...

  5. ISO 基础之 (十三) protocol 协议

    一 简绍 protocol,简单来说就是一系列不属于任何类的方法列表,其中声明的方法可以被任何类实现.这种模式一般称为代理(delegation)模式.通过Protocol定义各种行为,在不同的场景采 ...

  6. R语言实战读书笔记1—语言介绍

    第一章 语言介绍 1.1 典型的数据分析步骤 1.2 获取帮助 help.start() help("which") help.search("which") ...

  7. 【转】iOS开发-Protocol协议及委托代理(Delegate)传值

    原文网址:http://www.cnblogs.com/GarveyCalvin/p/4210828.html 前言:因为Object-C是不支持多继承的,所以很多时候都是用Protocol(协议)来 ...

  8. protocol(协议)

      可以用来声明一大堆方法(不能声明成员变量) 只要某个类遵守了这个协议,就相当于拥有这个协议中的所有方法声明 只要父类遵守了某个协议,就相当于子类也遵守了   //定义一个名叫MyProtocol的 ...

  9. OC语法10——@protocol协议,

    参考资料:博客 @protocol,协议: OC中protocol的含义和Java中接口的含义是一样的,它们的作用都是为了定义一组方法规范. 实现此协议的类里的方法,必须按照此协议里定义的方法规范来. ...

随机推荐

  1. c++ 子类与父类之间的类型转换

    子类与父类之间的类型转换 先给一段代码 class Base { public: int a = 10; }; class pub_Derv : public Base { Base *getBase ...

  2. CPU、进程、线程原理

    巨人的肩膀 看完这篇还不懂高并发中的线程与线程池你来打我 (qq.com)

  3. Linux系列——挂载Windows虚拟文件夹到Linux系统

    ​ 在windows操作系统上安装多台Linux虚拟机,需要方便的在windows系统和虚拟机上的Linux系统进行文件拷贝. 需要用到共享虚拟文件夹技术,将windows文件夹挂载到linux系统中 ...

  4. 单片机中volatile的应用

    01.简述 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了.精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使 ...

  5. 如何在Excel里安装excel插件?

    随着科技的发展,人们对数据分析的要求越来越多, Excel也存在一些问题,长期困扰一线业务用户:首先是性能问题.对于大数据量,Excel处理起来很慢.数据获取的过程麻烦,特别是周期性的数据获取,每次都 ...

  6. C#中String StringBuilder

    String和StringBuilder和StringBuffer,这三个都是值得深究一翻的,可能很多人会说,实在不行的话,都全部用StringBuilder,啥事没有,我不能说你的想法事不正确的,但 ...

  7. 《Selenium+Pytest Web自动化实战》随到随学在线课程,零基础也能学!

    课程介绍 课程主题:<Selenium+Pytest Web自动化实战> 适合人群: 1.功能测试转型自动化测试 2.web自动化零基础的小白 3.对python 和 selenium 有 ...

  8. 4.Profile(多环境配置)

    在实际的项目开发中,一个项目通常会存在多个环境,例如,开发环境.测试环境和生产环境等.不同环境的配置也不尽相同,例如开发环境使用的是开发数据库,测试环境使用的是测试数据库,而生产环境使用的是线上的正式 ...

  9. Qt:QJsonObject

    0.说明 QJsonObject在逻辑上就是一个Map或Dict!记住这一点对理解它的方法.说明很有帮助. QJsonObject类封装了JSON Object. JSON Object是一个Key- ...

  10. ELK日志收集(SpringBoot)

    目录 环境&准备 ES安装 Kibana安装 Logstash安装 Logstash配置 SpringBoot中logback-spring.xml配置 测试 启动 ES\Kibana\Log ...