Proto(协议缓冲区)

2021-01-31  本文已影响0人  ghostxbh

Proto(协议缓冲区)

简介

协议缓冲区是Google的与语言无关,与平台无关的可扩展机制,用于对结构化数据进行序列化–以XML为例,但更小,更快,更简单。您定义要一次构造数据的方式,然后可以使用生成的特殊源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。

协议缓冲区当前支持Java,Python,Objective-C和C ++生成的代码。使用我们新的proto3语言版本,您还可以使用Dart,Go,Ruby和C#,并提供更多语言。

定义消息体

// 登陆请求消息体
message LoginRequest {
    string username = 1;
    string password = 2;
}

分配字段编号

消息定义中的每个字段都有一个唯一的编号。
这些数字用于标识消息二进制格式的字段,一旦使用了消息类型,就不应更改这些数字。
注意,范围为1到15的字段编号需要一个字节来编码,包括字段编号和字段的类型(您可以在Protocol Buffer Encoding中找到有关此内容的更多信息)。16到2047之间的字段编号占用两个字节
因此,应该为非常频繁出现的消息元素保留字段编号1到15。记住要留出一些空间,以便将来可能会添加一些经常出现的元素。

您可以指定最小的场数是1,最大为2 ^29-1,或536870911。
您也不能使用数字19000到19999(FieldDescriptor::kFirstReservedNumber至FieldDescriptor::kLastReservedNumber),因为它们是为协议缓冲区实现保留的-如果您在中使用这些保留数之一,则协议缓冲区编译器会抱怨.proto。同样,您不能使用任何以前保留的字段号。

参数定义规则

为proto2数据类型前缀,proto3以简化

repeated int32 samples = 4 [packed=true];

在proto3中,repeated标量数字类型的字段packed默认情况下使用编码

保留字段

message Foo{
    reserved 2,15,9 to 11;
    reserved "foo","bar";
}

如果您通过完全删除字段或将其注释掉来更新消息类型,将来的用户可以在自己对类型进行更新时重用该字段号。
如果他们以后加载相同版本的旧版本,可能会导致严重的问题.proto,包括数据损坏,隐私错误等。
确保不会发生这种情况的一种方法是,将已删除字段的字段编号(和/或名称,也可能导致JSON序列化的问题)指定为reserved。
如果将来有任何用户尝试使用这些字段标识符,则协议缓冲区编译器会报错。
注意,您不能在同reserved一条语句中混用字段名称和字段编号。

数据类型

.proto Type Notes C++ Type Java Type Python Type[2] Go Type
double 浮点双精度 double double float *float64
float 浮点单精度 float float float *float32
int32 使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint32。 int32 int int *int32
int64 使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint64。 int64 long int/long[3] *int64
uint32 可变长度编码 uint32 int[1] int/long[3] *uint32
uint64 可变长度编码 uint64 long[1] int/long[3] *uint64
sint32 使用可变长度编码。有符号的int值。与常规int32相比,它们更有效地编码负数 int32 int int *int32
sint64 使用可变长度编码。有符号的int值。与常规int64相比,它们更有效地编码负数 int64 long int/long[3] *int64
fixed32 始终为四个字节。如果值通常大于228,则比uint32更有效。 uint32 int[1] int/long[3] *uint32
fixed64 始终为八个字节。如果值通常大于256,则比uint64更有效。 uint64 long[1] int/long[3] *uint64
sfixed32 始终为四个字节 int32 int int *int32
sfixed64 始终为八个字节 int64 long int/long[3] *int64
bool bool boolean bool *bool
string 字符串必须始终包含UTF-8编码或7位ASCII文本 string String unicode (Python 2) or str (Python 3) *string
bytes 可以包含任意字节序列 string ByteString bytes []byte

默认值

类型 默认值
string null
byte 空字节
boolean false
int 0
enum 第一个定义的枚举值, 0
message 取决语言

枚举

message UserInfoRequest{
    string id = 1;
    string name = 2;
    enum Gender{
        MAN = 0;
        WOMAN = 1;
        NONE = 2;
    }
    Gender gender = 3;
}
message Request1{
    enum MyEnum{
        option allow_alias = true;
        YES = 0;
        NO = 1;
        ANY = 2;
    }
}

message Request2{
    enum MyEnumTest{
        YES = 0;
        NO = 1;
    }
}

将相同的值分配给不同的枚举常量来定义别名。
将allow_alias选项设置为true,否则协议别名会在找到别名时生成一条错误消息。

使用其他类型

message SearchResponse{
    repeated Response res = 1;
}

message Response{
    int32 code = 1;
    string msg = 2;
    repeated string data = 3;
}

您可以将其他消息类型用作字段类型。
例如,假设你想包括Response每个消息的SearchResponse消息-要做到这一点,你可以定义一个Response在同一个消息类型.proto,然后指定类型的字段Response中SearchResponse

导入proto

import "proto/user.proto"
// 虚拟位置映射,转发改变过的地址
import public "new.proto"

协议编译器使用-I/ --proto_path标志在协议编译器命令行上指定的一组目录中搜索导入的文件。
如果未给出任何标志,它将在调用编译器的目录中查找。
通常,应将--proto_path标志设置为项目的根,并对所有导入使用完全限定的名称。

嵌套类型

message BookReq{
    message Book{
        int32 id = 1;
        string name = 2;
    }
    repeated Book book = 1;
}

或

book.proto
message BookReq{    Level 0
    message Book{   Level 1
        int32 id = 1;
        string name = 2;
    }
    repeated Book book = 1;
}

request.proto

import "book.proto"
message Req{
    BookReq.Book book = 1;
}

更新消息类型

如果现有消息类型不再满足您的所有需求(例如,您希望消息格式具有一个额外的字段),但是您仍然希望使用以旧格式创建的代码,请不要担心!在不破坏任何现有代码的情况下更新消息类型非常简单。只要记住以下规则:

不要更改任何现有字段的字段编号。
如果添加新字段,则仍可以使用新生成的代码来解析使用“旧”消息格式通过代码序列化的任何消息。您应记住这些元素的默认值,以便新代码可以与旧代码生成的消息正确交互。同样,新代码创建的消息也可以由旧代码解析:旧的二进制文件在解析时只会忽略新字段。有关详细信息,请参见“ 未知字段”部分。
只要在更新后的消息类型中不再使用字段号,就可以删除字段。您可能想要重命名该字段,或者添加前缀“ OBSOLETE_”,或者将字段编号保留,以使您的将来的用户.proto不会意外重用该编号。
int32,uint32,int64,uint64,和bool都是兼容的-这意味着你可以在现场从这些类型到另一种改变不破坏forwards-或向后兼容。如果从电线中解析出一个不适合相应类型的数字,则将获得与在C ++中将该数字强制转换为该类型一样的效果(例如,如果将64位数字读取为int32,它将被截断为32位)。
sint32并且sint64彼此兼容,但与其他整数类型不兼容。
string并且bytes只要字节是有效的UTF-8即可兼容。
bytes如果字节包含消息的编码版本,则嵌入式消息与之兼容。
fixed32与兼容sfixed32,并fixed64用sfixed64。
对于string,bytes和消息字段,optional与兼容repeated。给定重复字段的序列化数据作为输入,如果期望该字段为optional原始类型字段,则期望该字段的客户端将使用最后一个输入值;如果是消息类型字段,则将合并所有输入元素。请注意,这不是一般的数值类型,包括布尔变量和枚举安全。重复的数字类型字段可以以打包格式序列化,当需要一个optional字段时,将无法正确解析该格式。
enum与兼容int32,uint32,int64,和uint64电线格式条款(请注意,如果他们不适合的值将被截断)。但是请注意,客户端代码在反序列化消息时可能会以不同的方式对待它们:例如,无法识别的proto3 enum类型将保留在消息中,但是在反序列化消息时如何表示这取决于语言。Int字段始终只是保留其值。
将单个值更改为新 值的成员oneof是安全且二进制兼容的。oneof如果您确定没有代码一次设置多个字段,那么将多个字段移动到新字段中可能是安全的。将任何字段移到现有字段中oneof都是不安全的

未知字段

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

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

任何

import "google/protobuf/any.proto";

message LoginResponse{
  int32 code = 1;
  string msg = 2;
  repeated google.protobuf.Any data = 3;
}

Oneof

使用

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

将oneof字段添加到oneof定义。您可以添加任何类型的map字段,但字段和repeated字段除外。
oneof字段具有与常规字段相同的getter和setter。您还将获得一种特殊的方法来检查oneof中的哪个值(如果有)

功能

向后兼容问题

添加或删除字段之一时请多加注意。如果检查oneof的值返回None/ NOT_SET,则可能意味着尚未设置oneof或已将其设置为不同版本的oneof中的字段。由于无法知道导线上的未知字段是否是oneof的成员,因此无法分辨出差异。

标签重用问题

将字段移入或移出oneof:在对消息进行序列化和解析后,您可能会丢失一些信息(某些字段将被清除)。但是,您可以安全地将单个字段移动到新字段中,并且如果知道只设置了一个字段,则可以移动多个字段。
删除一个oneof字段并将其添加回:序列化和解析消息后,这可能会清除您当前设置的oneof字段。
拆分或合并其中一个:与移动常规字段有类似的问题。

Map

快捷语法:
map<key_type, value_type> map_field = N;

示例:
map<string, Result> results = 3;

其中key_type可以是任何整数或字符串类型(因此,除浮点类型和以外的任何标量类型bytes)。
请注意,enum无效key_type。的value_type可以是任何类型的除另一Map。

向后兼并

message ResultMap{
    key_type key = 1;
    value_type value = 2;
}

repeated ResultMap map = N;

防止协议消息类型冲突,可以定义包

package com.test;
message TestFoo{...}

message Test{
    com.test.TestFoo foo = 1;
}

定义服务

service BookService{
    rpc Search(BookReq) retruns (BookRes);
}

如果要将消息类型与RPC(远程过程调用)系统一起使用,则可以在.proto文件中定义RPC服务接口,并且协议缓冲区编译器将以您选择的语言生成服务接口代码和存根。

JSON对比

json
json

自定义选项

option java_package = "com.test.book";
option java_multiple_files = true;
option java_outer_classname = "Ponycopter";
option optimize_for = SPEED;
int32 old_field = 6 [deprecated = true];

资源与版本


收录时间: 2020/08/19
原文地址:Proto协议缓冲区

上一篇 下一篇

猜你喜欢

热点阅读