proto 语法

发布时间: 更新时间: 总字数:1273 阅读时间:3m 作者: IP属地: 分享 复制网址

Protocol Buffer proto 文件语法规则介绍

编译工具

.proto 文件建议使用 Visual Studio Code proto 文件编辑插件:vscode-proto3

示例 proto 文件

// See README.md for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials.  They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials

// [START declaration]
syntax = "proto3";  // protobuf 版本
package tutorial;  // 包名,隔离不同包 message 命名冲突问题

import "google/protobuf/timestamp.proto";  // 引入其他 proto 文件,可以使用 public、weak 关键字修饰
// [END declaration]

// [START java_declaration]
option java_multiple_files = true;  // option 定义的可选字段
option java_package = "com.example.tutorial.protos";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]

// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]

// [START go_declaration]
option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";
// [END go_declaration]

// [START messages]
message Person {  // 定义 person message 类型对应的字段,会被 protoc 编译为不同语言的对应对象,如Java中的class、Go中的struct
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}
// [END messages]

说明

  • 命名:字段名、消息名、枚举类型名、map名、服务名等首字母必须是数据,后接字母、数字、或下划线 _
  • message 的字段可以使用 repeated 修饰,表示重复,对应 Golang 的数组类型
    • 如:message xxx { repeated double Field1 = 1; ...}
  • Oneof 一组字段最多允许一个字段出现
  • map 键值类型:map<keyType, valueType> mapName = fieldNumber [ fieldOptions ]
  • deprecated=true 跟在字段后边,标识该字段已废弃

message 字段类型

  • 数字类型:double、float、int32、int64、uint32、uint64、sint32、sint64
    • 存储长度可变的浮点数、整数、无符号整数和有符号整数
  • 存储固定大小的数字类型:fixed32、fixed64、sfixed32、sfixed64
    • 存储空间固定
  • 布尔类型: bool
  • 字符串: string
  • bytes: 字节数组
  • messageType: 消息类型
  • enumType: 枚举类型

Reserved 忽略字段

  • Reserved 在 message 中忽略某些字段,如下
syntax = "proto3";

package abc;

message AllNormalypes {
  reserved 2, 4 to 6;
  reserved "field8", "field10";
  double field1 = 1;
  // float field2 = 2;
  int32 field3 = 3;
  // int64 field4 = 4;
  // uint32 field5 = 5;
  // uint64 field6 = 6;
  sint32 field7 = 7;
  // sint64 field8 = 8;
  fixed32 field9 = 9;
  // fixed64 field10 = 10;
  sfixed32 field11 = 11;
}

enum 枚举类型

用来限定字段的取值范围,可以单独定义,也可以在 message 中:

enum PhoneType {
    // option allow_alias = true;  // 是否允许字段编号重复
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}

packed

  • packed 修饰 repeated 字段或基本类型的 repeated 字段
    • 传统的多个 T-V-T-V-T-V 对存储
    • packed=truerepeated 字段存储方式,T-L-V-V-V

其他

  • message 字段类型也可以是另一个 message
  • message 可以嵌套
  • google.protobuf.Any 字段允许处理嵌套数据
  • 时间
    • github.com/golang/protobuf/ptypes/timestamp.Timestamp: time.Time
    • github.com/golang/protobuf/ptypes/duration.Duration:time.Duration
  • service 定义 rpc 接口

编译

#!/bin/bash

SRC_DIR=.
DST_DIR=.

protoc \
  -I=$SRC_DIR \
  -I/usr/local/include \
  -I=$GOPATH/src \
  --go_out=$DST_DIR \
  # --plugin=$GOBIN/protoc-gen-go \
  $SRC_DIR/proto/addressbook.proto

说明:

  • -I protoc 搜索 import proto 的文件夹
  • --*_out 目标代码输出目录,根据编程语言选择:
    • --go_out Golang 代码
    • --cpp_out C++ 代码
    • --java_out Java 代码
    • --python_out Python 代码
    • --csharp_out C# 代码
    • --objc_out ObjectC 代码
    • --ruby_out Ruby 代码
    • --pho_out PHP 代码
  • 生成的数据结构会包括类和对应的方法,如 GetXXX()

序列化和反序列化

使用 proto.Marshalproto.Unmarshal 方法

  • 实例化
p := pb.Person{
        Id:    1234,
        Name:  "John Doe",
        Email: "jdoe@example.com",
        Phones: []*pb.Person_PhoneNumber{
                {Number: "555-4321", Type: pb.Person_HOME},
        },
}
  • Writing a Message
book := &pb.AddressBook{}
// ...

// Write the new address book back to disk.
out, err := proto.Marshal(book)
if err != nil {
        log.Fatalln("Failed to encode address book:", err)
}
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
        log.Fatalln("Failed to write address book:", err)
}
  • Read the existing address book.
in, err := ioutil.ReadFile(fname)
if err != nil {
        log.Fatalln("Error reading file:", err)
}
book := &pb.AddressBook{}
if err := proto.Unmarshal(in, book); err != nil {
        log.Fatalln("Failed to parse address book:", err)
}

详情参考:

参考

  1. https://protobuf.dev
  2. Golang 使用 protobuf 实现 grpc 通信
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数