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

指定版本

protocol 语言文件后面名为.proto

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

syntax = "proto3";

定义Message

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

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

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

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

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

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

复杂类型字段声明

message SearchResponse {
repeated Result results = 1;
} message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}

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

保留字段

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

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

也可以将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关键字定义枚举,并什么可以有哪些值。

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

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

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

message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}

allow_alias

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

enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}

保留值

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

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

包(命名空间)声明

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

package foo.bar;

包引入

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

import "google/api/annotations.proto";

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

import public

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

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

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

嵌套类型

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

message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}

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

message SomeOtherMessage {
SearchResponse.Result result = 1;
}

更新消息

如果有对现有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包。

import "google/protobuf/any.proto";

message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}

OneOf

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

message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}

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

SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name field.
CHECK(!message.has_name());

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

Maps

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

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

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

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

message Order {
message Attributes {
map<string, string> values = 1;
}
repeated Attributes attributes = 1;
}

或者

message MapFieldEntry {
key_type key = 1;
value_type value = 2;
} repeated MapFieldEntry map_field = N;

服务定义

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

service BlogService {
rpc CreateArticle (CreateArticleRequest) returns (CreateArticleReply) {
option (google.api.http) = {
post: "/v1/article/"
body: "*"
};
}
}

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. Solution -「ARC 104C」Fair Elevator

    \(\mathcal{Description}\)   Link.   数轴从 \(1\sim 2n\) 的整点上有 \(n\) 个闭区间.你只知道每个区间的部分信息(可能不知道左或右端点,或者都不知 ...

  2. 2020年的第一天-我的IDEA出现This license ... has been cancelled

    IDEA激活在1月3日的早上,激活码被取消了.提示:This license ... has been cancelled. 经过查询.解决方法教程无非是. ¥%--&*(激活码... 210 ...

  3. 关于oracle中(+)的运用

    一.基础 1.1 SQL查询的基本原理 第一.单表查询:根据WHERE条件过滤表中的记录,形成中间表(这个中间表对用户是不可见的):然后根据SELECT的选择列选择相应的列进行返回最终结果.第二.两表 ...

  4. pytest--fixture基本使用(主要用来进行测试环境的初始化和清理,fixture中的params参数还可以用来进行参数化)

    fixture fixture修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作. 方法: fixture(scope=" ...

  5. [题解]UVA11029 Leading and Trailing

    链接:http://vjudge.net/problem/viewProblem.action?id=19597 描述:求n^k的前三位数字和后三位数字 思路:题目要解决两个问题.后三位数字可以一边求 ...

  6. Linux 中CPU 和 GPU 的行为监控

    由于 Steam(包括 Steam Play,即 Proton)和一些其他的发展,GNU/Linux 正在成为越来越多计算机用户的日常游戏平台的选择.也有相当一部分用户在遇到像视频编辑或图形设计等(K ...

  7. containerd与kubernetes集成部署

    概念介绍 cri (Container runtime interface) cri is a containerd plugin implementation of Kubernetes conta ...

  8. C#索引器-有参属性

    总结 只要类中有类似于属性的元素就应创建索引器,此属性代表的不是一个值,而是值的集合,其中每一个项由一组参数标识. 这些参数可以唯一标识应引用的集合中的项. 索引器延伸了属性的概念,索引器中的一个成员 ...

  9. Debian 11 配置优化指南

    原文地址: Debian 11 配置优化指南 - WindSpiritIT 0x00 简介 本文仅适用于配置 Debian 11 Bullseye 文中同时包含 Gnome 桌面和 KDE 桌面配置, ...

  10. Chapter06 数组(Array)

    目录 Chapter06 数组 6.1 数组的认识 6.2 数组的使用 使用方式1 - 动态初始化 使用方式2 - 动态初始化 使用方法3 - 静态初始化 6.3 数组使用的注意事项和细节 6.4 数 ...