proto使用说明

1 篇文章 0 订阅

proto使用说明

一、语法
  • 指定proto语法版本

在proto文件第一行添加:

syntax= "proto3"

目前proto有两个版本proto2和proto3,两者语法有一定的差异,在选择的时候注意一下

  • 定义message

(1)定义: 一个message相当于java中的实体类,里面定义了不同数据类型的数据,并且在结尾处标上标签序号
(2)嵌套:一个message内部也可以定义message类型的数据
(3)字段格式:修饰符 参数类型 参数名称=字段编码值 [字段默认值] (这里是proto2的语法规则)
(4)proto3字段格式特性:不允许加修饰符(只有两个修饰符),不允许加字段默认值

在proto2中修饰符有以下几种:

required: 格式良好的消息必须恰好具有该字段之一。
optional:一个格式良好的消息可以有零个或一个这个字段(但不能超过一个)。
repeated:该字段可以在消息中重复任意次数(包括零次)。重复值的顺序将被保留。也就是表示一个数组

proto2中的修饰符有:

singular:默认
repeated:该字段可以在格式良好的消息中重复任意次数(包括零次)。重复值的顺序将被保留。

例子:

message Reply{
 // 修饰符 参数类型 参数名称=标识符 [字段默认值]
    bool Result = 1;
    string UUID = 2;
    string Token = 3;
  	repeated  string extra = 4;

    //这里定义了一个Info message格式的数据
    Info info = 5;
}

message Info{
    string id = 1;
    string name = 2;
    int32 age = 3;
}

//定义枚举类型 第一个值必须是0
enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  • service定义

rpc通信原理分析参考:https://www.cnblogs.com/aspirant/p/8603028.html

在这里插入图片描述

(1) 定义访问服务端的函数名称,传递参数,返回值

— 就是rpc通信中构建请求消息结构的 接口名称

(2) 一个service可以定义多个待调用的函数

(3) 定义方式:rpc 方法名 (参数) returns (返回值) 没有返回值 就写Empty

例子:

service PersonService {
  //添加
  rpc Add (Info) returns (Empty) {
  }
  //替换
  rpc Replace (Info) returns (Empty) {
  }
  //查询
  rpc Search (SearchInfo) returns (stream Info) {
  }
  //获取总数
  rpc Count (SearchInfo) returns (CountInfo) {
  }
  //删除
  rpc Delete (Id) returns (Empty) {
  }
  //导出
  rpc Export (Ids) returns (Id) {
  }
}
  • 保留标识符(reserved)

message中每一个字段都对应有一个标识符(1,2,3,4,5,6…), 当版本更新的时候,删除或者注释掉某一个字段的时候,保留标识符可以让这个标识号不会被新的字段名称使用,这样避免了bug等等

例如:在上面的Info这个message中,age这个字段的标识符是3,如果在下一个版本V2中,我们将age删除了,不要了,添加了一个叫sex的字段
(1)如果我们对3这个标识符做保留操作, sex这个字段标识符不能是3,否则编译不通过,这样是正确的,合理的;
(2)如果我们对3这个标识符不做保留操作,sex这个字段标识符或者其它新的字段使用3这个标识符,会导致前后V1和V2两个版本3这个标识符代表的字段不一致,出现其它Bug

例子

message Persion{
    string id = 1;
    string name = 2;

    //保留3,15,9,10,11这几个标识符不能被使用
    reserved  3, 15, 9 to 11;
    //reserved "location"
}
  • 数据格式和Java语言格式对应表
protojava说明
doubledouble
floatfloat
int32int使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代
uint32int使用变长编码
uint64long使用变长编码
sint32int使用变长编码,这些编码在负值时比int32高效的多
sint64long使用变长编码,有符号的整型值。编码时比通常的int64高效。
sfixed32int总是4个字节
sfixed64long总是8个字节
fixed32int总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。
fixed64long总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。
boolboolean
stringString一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
bytesByteString可能包含任意顺序的字节数据。

还支持了枚举类型

  • 数据格式默认值

(1)对于strings,默认是一个空string
(2)对于bytes,默认是一个空的bytes
(3)对于bools,默认是false
(4)对于数值类型,默认是0
(5)对于枚举,默认是第一个定义的枚举值,必须为0;
(6)对于消息类型(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;
}

可以将option属性中allow_alias 设置为true,来为枚举类型定义相同的常量值

message MyMessage1 {
  enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
message MyMessage2 {
  enum EnumNotAllowingAlias {
    UNKNOWN = 0;
    STARTED = 1;
    // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  }
}

枚举常量值必须在32字节integer范围内。负数效率低,不推荐使用

  • 导包定义

如果想使用的消息类型定义在另一个.proto文件中,可以通过导包的方式将另一个文件导入进来,导包的语句如下:

import "myproject/other_protos.proto";

场景使用:test.proto中调用test2.proto文件中的DeviceInfo这个message,test2.proto的包名是 test2package
(1)import “test2.proto”; 添加你需要调用哪一个proto文件
(2)在test.proto中调用: test2package.DeviceInfo deviceInfo = 3;
例子:

syntax = "proto3";
package testpackge;

//导包
import "test2.proto"
message Info{
    string id = 1;

    //调用Message
    test2package.DeviceInfo deviceInfo = 2;
}
  • option可选项

//是否运行生成多个java文件
option java_multiple_files = false;

//这个选项表明生成java类所在的包。如果在.proto文件中没有明确的声明java_package,就采用默认的包名
option java_package = “com.example.administrator.grpctest.proto”;

//生成的java类名字
option java_outer_classname = “TestProto”;

//生成方式:可以被设置为 SPEED, CODE_SIZE,or LITE_RUNTIME。这些值将通过如下的方式影响C++及java代码的生成:
option optimize_for = SPEED;

  • (1)SPEED (default): protocol buffer编译器将通过在消息类型上执行序列化、语法分析及其他通用的操作。这种代码是最优的。

  • (2)CODE_SIZE: protocol buffer编译器将会产生最少量的类,通过共享或基于反射的代码来实现序列化、语法分析及各种其它操作。采用该方式产生的代码将比SPEED要少得多, 但是操作要相对慢些。当然实现的类及其对外的API与SPEED模式都是一样的。这种方式经常用在一些包含大量的.proto文件而且并不盲目追求速度的 应用中。

  • (3)LITE_RUNTIME: protocol buffer编译器依赖于运行时核心类库来生成代码(即采用libprotobuf-lite 替代libprotobuf)。这种核心类库由于忽略了一 些描述符及反射,要比全类库小得多。这种模式经常在移动手机平台应用多一些。编译器采用该模式产生的方法实现与SPEED模式不相上下,产生的类通过实现 MessageLite接口,但它仅仅是Messager接口的一个子集

  • 更新一个消息类型

如果一个已有的消息格式已无法满足新的需求——如,要在消息中添加一个额外的字段——但是同时旧版本写的代码仍然可用。不用担心!更新消息而不破坏已有代码是非常简单的。在更新时只要记住以下的规则即可。

  • 不要更改任何已有的字段的数值标识。
  • 如果你增加新的字段,使用旧格式的字段仍然可以被你新产生的代码所解析。你应该记住这些元素的默认值这样你的新代码就可以以适当的方式和旧代码产生的数据交互。相似的,通过新代码产生的消息也可以被旧代码解析:只不过新的字段会被忽视掉。注意,未被识别的字段会在反序列化的过程中丢弃掉,所以如果消息再被传递给新的代码,新的字段依然是不可用的(这和proto2中的行为是不同的,在proto2中未定义的域依然会随着消息被序列化)
  • 非required的字段可以移除——只要它们的标识号在新的消息类型中不再使用(更好的做法可能是重命名那个字段,例如在字段前添加“OBSOLETE_”前缀,那样的话,使用的.proto文件的用户将来就不会无意中重新使用了那些不该使用的标识号)。
  • int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。
  • sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。
  • string和bytes是兼容的——只要bytes是有效的UTF-8编码。
  • 嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。
  • fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。
  • 枚举类型与int32,uint32,int64和uint64相兼容(注意如果值不相兼容则会被截断),然而在客户端反序列化之后他们可能会有不同的处理方式,例如,未识别的proto3枚举类型会被保留在消息中,但是他的表示方式会依照语言而定。int类型的字段总会保留他们的
  • Map(映射)

如果你希望创建一个关联映射,protocol buffer提供了一种快捷的语法:

map<key_type, value_type> map_field = N;1

其中key_type可以是任意Integer或者string类型(所以,除了floating和bytes的任意标量类型都是可以的)value_type可以是任意类型。

例如,如果你希望创建一个project的映射,每个Projecct使用一个string作为key,你可以像下面这样定义:

map<string, Project> projects = 3;
  • Map的字段可以是repeated。
  • 序列化后的顺序和map迭代器的顺序是不确定的,所以你不要期望以固定顺序处理Map
  • 当为.proto文件产生生成文本格式的时候,map会按照key 的顺序排序,数值化的key会按照数值排序。
  • 从序列化中解析或者融合时,如果有重复的key则后一个key不会被使用,当从文本格式中解析map时,如果存在重复的key。
  • JSON映射

Proto3 支持JSON的编码规范,使他更容易在不同系统之间共享数据,在下表中逐个描述类型。

如果JSON编码的数据丢失或者其本身就是null,这个数据会在解析成protocol buffer的时候被表示成默认值。如果一个字段在protocol buffer中表示为默认值,体会在转化成JSON的时候编码的时候忽略掉以节省空间。具体实现可以提供在JSON编码中可选的默认值。

proto3JSONJSON示例注意
messageobject{“fBar”: v, “g”: null, …}产生JSON对象,消息字段名可以被映射成lowerCamelCase形式,并且成为JSON对象键,null被接受并成为对应字段的默认值
enumstring“FOO_BAR”枚举值的名字在proto文件中被指定
mapobject{“k”: v, …}所有的键都被转换成string
repeated Varray[v, …]null被视为空列表
booltrue, falsetrue, false
stringstring“Hello World!”
bytesbase64 string“YWJjMTIzIT8kKiYoKSctPUB+”
int32, fixed32, uint32number1, -10, 0JSON值会是一个十进制数,数值型或者string类型都会接受
int64, fixed64, uint64string“1”, “-10”JSON值会是一个十进制数,数值型或者string类型都会接受
float, doublenumber1.1, -10.0, 0, “NaN”, “Infinity”JSON值会是一个数字或者一个指定的字符串如”NaN”,”infinity”或者”-Infinity”,数值型或者字符串都是可接受的,指数符号也可以接受
Anyobject{“@type”: “url”, “f”: v, … }如果一个Any保留一个特上述的JSON映射,则它会转换成一个如下形式:{"@type": xxx, "value": yyy}否则,该值会被转换成一个JSON对象,@type字段会被插入所指定的确定的值
Timestampstring“1972-01-01T10:00:20.021Z”使用RFC 339,其中生成的输出将始终是Z-归一化啊的,并且使用0,3,6或者9位小数
Durationstring“1.000340012s”, “1s”生成的输出总是0,3,6或者9位小数,具体依赖于所需要的精度,接受所有可以转换为纳秒级的精度
Structobject{ … }任意的JSON对象,见struct.proto
Wrapper typesvarious types2, “2”, “foo”, true, “true”, null, 0, …包装器在JSON中的表示方式类似于基本类型,但是允许nulll,并且在转换的过程中保留null
FieldMaskstring“f.fooBar,h”见fieldmask.proto
ListValuearray[foo, bar, …]
Valuevalue任意JSON值
NullValuenullJSON null
二、proto使用示例
syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.example.administrator.grpctest.proto";
option java_outer_classname = "HelloWorldProto";
option optimize_for = CODE_SIZE;

//service package name
package proto;

//服务端中的HelloWorld这个接口,这个接口中可以包含多个方法
service HelloWorld {
  rpc sayHello (RequestP) returns (ReplyP) {}
}

message ReplyP{
    bool Result = 1;
    string UUID = 2;
    string Token = 3;
    string extra = 4;
}

message RequestP{
    string UUID = 1;
    string Token = 2;
    PersionInfo persionInfo = 3;
    reserved  15, 9 to 11;
}

message PersionInfo{
    string id = 1;
    string name = 2;
    int32 age = 3;
    enmu Sex{
        int32 m = 0;
        int32 w = 1;
    }
    Sex sex = 4;
}

ring Token = 3;
string extra = 4;
}

message RequestP{
string UUID = 1;
string Token = 2;
PersionInfo persionInfo = 3;
reserved 15, 9 to 11;
}

message PersionInfo{
string id = 1;
string name = 2;
int32 age = 3;
enmu Sex{
int32 m = 0;
int32 w = 1;
}
Sex sex = 4;
}


  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
proto3是Google开发的一种语言无关的数据序列化协议,用于在不同的平台和编程语言之间进行数据交换。下面是proto3的写法详细说明: 1. 定义message message是proto3中最基本的数据结构,类似于其他编程语言中的类或结构体,用于定义一组相关的数据字段。下面是message的语法格式: ``` syntax = "proto3"; message MessageName { //字段定义 } ``` 其中,syntax指定使用proto3版本;MessageName是message的名称,可以自定义;字段定义则包括字段类型、字段名称和字段编号。 2. 定义字段 在message中定义的字段可以是以下类型: - bool:布尔值 - int32、int64、uint32、uint64、sint32、sint64:整数类型 - float、double:浮点数类型 - string:字符串类型 - bytes:字节流类型 - 枚举类型 - message类型(嵌套message) 下面是字段定义的语法格式: ``` 字段类型 字段名称 = 字段编号; ``` 其中,字段类型和字段名称都是必须的,字段编号则是可选的。字段编号是为了在数据传输时压缩数据大小而引入的,建议在定义时为每个字段都指定一个编号。 3. 定义枚举类型 枚举类型也是一种常见的数据类型,用于定义一组可能的取值。下面是枚举类型的语法格式: ``` enum EnumName { ENUM_VALUE1 = 0; ENUM_VALUE2 = 1; ... } ``` 其中,EnumName是枚举类型的名称,可以自定义;ENUM_VALUE1、ENUM_VALUE2等则是枚举值的名称,也可以自定义;枚举值后面的数字则是枚举值的编号。 4. 定义服务 除了定义数据结构,proto3还可以用于定义服务。服务是一组提供特定功能的RPC(远程过程调用)方法的集合。下面是服务的语法格式: ``` service ServiceName { rpc MethodName(RequestType) returns (ResponseType) {} ... } ``` 其中,ServiceName是服务的名称,可以自定义;MethodName是RPC方法的名称,也可以自定义;RequestType和ResponseType分别是RPC方法的请求和响应类型,必须是已经定义的message类型。 以上就是proto3的基本语法。在实际使用中,可以使用proto3编译器将.proto文件编译成各种编程语言的代码,以便在不同的平台和编程语言之间进行数据交换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值