zoukankan      html  css  js  c++  java
  • Proto3语法入门

    定义消息类型

    首先让看一个非常简单的例子。假设要定义搜索请求消息格式,其中每个搜索请求都有一个查询字符串、感兴趣的特定结果页面以及每页的多个结果。这是.proto用来定义消息类型的文件。

    syntax = "proto3";
    
    message SearchRequest {
      string query = 1;
      int32 page_number = 2;
      int32 result_per_page = 3;
    }
    
    • 该文件的第一行指定正在使用proto3语法:如果不这样做,协议缓冲区编译器将假定使用的是proto2。这必须是文件的第一个非空、非注释行。
    • 所述SearchRequest消息定义指定了三个字段(名称/值对),一个用于每条数据要在此类型的消息包括。每个字段都有一个名称和一个类型。

    指定字段规则

    消息字段可以是以下之一:

    • 单数:格式正确的消息可以有0个或一个该字段(但不超过一个)。这是 proto3 语法的默认字段规则。
    • repeated:该字段可以在格式良好的消息中重复任意次数(包括0)。重复值的顺序将被保留。

    在proto3中,标量数值类型的重复字段默认使用打包编码。

    message 的定义语法:

    <comment>
    
    message  <message_name> {
      <filed_rule>  <filed_type> <filed_name> = <field_number> 
           规则          类型          名称           编号  
    }
    
    • comment: 注射 /* */或者 //
    • message_name: 同一个pkg内,必须唯一
    • filed_rule: 可以没有, 常用的有repeated, oneof
    • filed_type: 数据类型, protobuf定义的数据类型, 生产代码的会映射成对应语言的数据类型
    • filed_name: 字段名称, 同一个message 内必须唯一
    • field_number: 字段的编号, 序列化成二进制数据时的字段编号

    分配字段编号

    消息定义中的每个字段都有一个唯一的编号。这些字段编号用于在消息二进制格式中标识字段,一旦消息类型被使用,就不应更改。

    注意:1 到 15 范围内的字段编号占用一个字节进行编码,包括字段编号和字段类型。16 到 2047 范围内的字段编号占用两个字节。

    指定的最小字段编号为 1,最大字段编号为 229 - 1 或 536,870,911。不能使用数字 19000 到 19999,因为它们是为 Protocol Buffers 实现保留的

    如果你想保留一个编号,以备后来使用可以使用 reserved 关键字声明

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

    值类型

    消息字段可以具有以下类型之一:

    上面就是所有的protobuf基础类型, 光有这些基础类型是不够的, 下面是protobuf提供的一些复合类型

    枚举类型

    使用enum来声明枚举类型:

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

    枚举声明语法:

    enum <enum_name> {
        <element_name> = <element_number>
    }
    
    • enum_name: 枚举名称
    • element_name: pkg内全局唯一, 很重要
    • element_number: 必须从0开始, 0表示类型的默认值, 32-bit integer

    别名

    如果的确有2个同名的枚举需求: 比如 TaskStatus 和 PipelineStatus 都需要Running,就可以添加一个: 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.
      }
    }
    

    预留值

    可以使用max关键字指定保留的数值范围达到最大可能值。

    enum Foo {
      UNIVERSAL = 0;
      WEB = 1;
      // IMAGES = 2; //Enum value 'IMAGES' uses reserved number 2
      YOUTUBE = 3; 
      reserved 2, 15, 9 to 11, 40 to max;
      reserved "FOO", "BAR";
    }
    

    同理枚举也支持预留值

    数组类型

    message  Result {
      int32 age = 1;
      string name = 2;
    }
    
    message SearchResponse {
      repeated Result result = 1;
    }
    
    // protoc -I=./ --go_out=./pbrpc/service --go_opt=module="github.com/ProtoDemo/pbrpc/service" pbrpc/service/test.proto
    // 会编译为:
    //type Result struct {
    // ... ...
    //	Age  int32  `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`
    //	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
    //}
    
    //type SearchResponse struct {
    // ... ...
    //	Result []*Result `protobuf:"bytes,1,rep,name=result,proto3" json:"result,omitempty"`
    //}
    

    Map

    如果想声明一个map, 可以如下进行:

    message Project {
        int32 age = 1;
        string name = 2;
    }
    
    message MapData {
      map<string, Project> projects = 1;
    }
    
    // protoc -I=./ --go_out=./pbrpc/service --go_opt=module="github.com/ProtoDemo/pbrpc/service" pbrpc/service/test.proto
    // projects map[string, Project]
    // 会编译为:
    //type Project struct {
    // ... ...
    //	Age  int32  `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`
    //	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
    //}
    
    //type MapData struct {
    // ... ...
    //	Projects map[string]*Project `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
    //}
    
    

    protobuf 声明map的语法:

    map<key_type, value_type> map_field = N;
    

    Oneof

    很像范型 比如 test_oneof 字段的类型 必须是 string name 和 SubMessage sub_message 其中之一:

    message Sub1 {
        string name = 1;
    }
    
    message Sub2 {
        string name = 1;
    }
    
    message SampleMessage {
        oneof test_oneof {
            Sub1 sub1 = 1;
            Sub2 sub2 = 2;
        }
    }
    
    // protoc -I=./ --go_out=./pbrpc/service --go_opt=module="github.com/ProtoDemo/pbrpc/service" pbrpc/service/test.proto
    // 会编译为:
    //type SampleMessage struct {
    //	state         protoimpl.MessageState
    //	sizeCache     protoimpl.SizeCache
    //	unknownFields protoimpl.UnknownFields
    //
    //	// Types that are assignable to TestOneof:
    //	//	*SampleMessage_Sub1
    //	//	*SampleMessage_Sub2
    //	TestOneof isSampleMessage_TestOneof `protobuf_oneof:"test_oneof"`
    //}
    
    // 操作使用
    // of := &pb.SampleMessage{}
    // of.GetSub1()
    // of.GetSub2()
    

    Any

    当无法明确定义数据类型的时候, 可以使用Any表示:

    import "google/protobuf/any.proto";
    
    message ErrorStatus {
      string message = 1;
      repeated google.protobuf.Any details = 2;
    }
    
    
    // protoc -I=./ -I=/usr/local/include --go_out=./pbrpc/service --go_opt=module="github.com/ProtoDemo/pbrpc/service" pbrpc/service/test.proto
    // 会编译为:
    // any本质上就是一个bytes数据结构
    //type ErrorStatus struct {
    //	state         protoimpl.MessageState
    //	sizeCache     protoimpl.SizeCache
    //	unknownFields protoimpl.UnknownFields
    //
    //	Message string       `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
    //	Details []*anypb.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
    //}
    
    

    any的定义

    // `Any` contains an arbitrary serialized protocol buffer message along with a
    // URL that describes the type of the serialized message.
    //
    // Protobuf library provides support to pack/unpack Any values in the form
    // of utility functions or additional generated methods of the Any type.
    //  Example 4: Pack and unpack a message in Go
    //
    //      foo := &pb.Foo{...}
    //      any, err := anypb.New(foo)
    //      if err != nil {
    //        ...
    //      }
    //      ...
    //      foo := &pb.Foo{}
    //      if err := any.UnmarshalTo(foo); err != nil {
    //        ...
    //      }
    //
    // The pack methods provided by protobuf library will by default use
    // 'type.googleapis.com/full.type.name' as the type URL and the unpack
    // methods only use the fully qualified type name after the last '/'
    // in the type URL, for example "foo.bar.com/x/y.z" will yield type
    // name "y.z".
    //
    //
    // JSON
    // ====
    // The JSON representation of an `Any` value uses the regular
    // representation of the deserialized, embedded message, with an
    // additional field `@type` which contains the type URL. Example:
    //
    //     package google.profile;
    //     message Person {
    //       string first_name = 1;
    //       string last_name = 2;
    //     }
    //
    //     {
    //       "@type": "type.googleapis.com/google.profile.Person",
    //       "firstName": <string>,
    //       "lastName": <string>
    //     }
    //
    // If the embedded message type is well-known and has a custom JSON
    // representation, that representation will be embedded adding a field
    // `value` which holds the custom JSON in addition to the `@type`
    // field. Example (for message [google.protobuf.Duration][]):
    //
    //     {
    //       "@type": "type.googleapis.com/google.protobuf.Duration",
    //       "value": "1.212s"
    //     }
    //
    type Any struct {
    	state         protoimpl.MessageState
    	sizeCache     protoimpl.SizeCache
    	unknownFields protoimpl.UnknownFields
    
      ...
    	// Note: this functionality is not currently available in the official
    	// protobuf release, and it is not used for type URLs beginning with
    	// type.googleapis.com.
    	//
    	// Schemes other than `http`, `https` (or the empty scheme) might be
    	// used with implementation specific semantics.
    	//
    	TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
    	// Must be a valid serialized protocol buffer of the above specified type.
    	Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
    }
    

    类型嵌套

    可以再message里面嵌套message

    message Outer {                  // Level 0
      message MiddleAA {  // Level 1
        message Inner {   // Level 2
          int64 ival = 1;
          bool  booly = 2;
        }
      }
      message MiddleBB {  // Level 1
        message Inner {   // Level 2
          int32 ival = 1;
          bool  booly = 2;
        }
      }
    }
    

    与Go结构体嵌套一样, 但是不允许 匿名嵌套, 必须指定字段名称

    引用包

    import "google/protobuf/any.proto";
    

    上面这情况就是读取的标准库, 在安装protoc的时候, 已经把改lib 挪到usr/local/include下面了,所以可以找到

    如果proto文件并没有在/usr/local/include目录下, 如何导入,比如:

    import "myproject/other_protos.proto";
    

    通过-I 可以添加搜索的路径, 这样就编译器就可以找到引入的包了

    引入后通过包的名称.变量的方式使用

    比如要应用该结构中的ErrorStatus

    syntax = "proto3";
    
    import "google/protobuf/any.proto";
    
    package hello;
    option go_package = "github.com/ProtoDemo/pbrpc/service";
    
    message ErrorStatus {
      string message = 1;
      repeated google.protobuf.Any details = 2;
    }
    

    引入ErrorStatus

    syntax = "proto3";
    
    // 由于这个文件的pkg 也叫hello, 因此可以不用添加 pkg前缀
    //  如果不是同一个pkg 就需要添加 pkg名称前缀, 比如hello.ErrorStatus
    
    import "pbrpc/service/test.proto";
    package hello;
    
    option go_package = "github.com/ProtoDemo/pbrpc/service";
    
    message ErrorStatusExt {
      ErrorStatus error_status = 1;
    }
    
    // protoc -I=./ -I=/usr/local/include --go_out=./pbrpc/service --go_opt=module="github.com/ProtoDemo/pbrpc/service" pbrpc/service/hello.proto
    

    更新规范

    • Don't change the field numbers for any existing fields.

    更多请参考 Updating A Message Type

    参考

  • 相关阅读:
    vue-cli3搭建可视化项目架构
    select框修改默认样式
    修改checkbox的默认样式
    input和textarea修改placehold的文字颜色
    checkbox更改默认样式,以及选中文字也可以选中checkbox
    自封装 ajax 函数,ajax原理
    git@github.com: Permission denied (publickey)
    EAS webservice安全模式
    DB2 根据id查表
    office web apps server安装
  • 原文地址:https://www.cnblogs.com/remixnameless/p/15665313.html
Copyright © 2011-2022 走看看