【Protobuf】Protobuf中的Message语法规范

简介: 在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32、string、bool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。

基本语法


syntax = "proto3";
message MessageName {
    FieldType fieldName = fieldNumber;
}


 syntax指定使用的Protobuf版本。在这个示例中,使用的是Protobuf 3版本。


 代码定义了一个Message,名称为MessageName,可以根据需要自定义Message的名称。


 在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32、string、bool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。


 例如:


syntax = "proto3";
message Email{
    int32 id = 1;
    string name = 2;
    string emails = 3;
}


标识号


 在Protobuf中,每个字段都需要一个唯一的标识号(field number),用于在消息的二进制编码中标识该字段。标识号的作用是确保在消息的编解码过程中能够准确地识别每个字段。


 以下是关于标识号的一些重要点:


 标识号是一个正整数。

 标识号的范围是1到2^29 - 1(Protobuf 3版本中是1到536,870,911)。

 在同一个Message中,每个字段的标识号必须是唯一的。

 标识号的选择是自由的,但应保持一致性和稳定性,避免频繁变更标识号。

 在编码过程中,每个字段都会与其标识号一起被序列化到二进制数据中。接收方在解码时,通过读取二进制数据中的标识号来正确识别和解析每个字段的值。


[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的字段预留一些标识号。



  在Protobuf中,包package用于对消息类型进行组织和命名空间管理。包的作用是确保消息类型的唯一性,并避免命名冲突。


  示例:


syntax = "proto3";
package com.example.mypackage;
message Email{
    int32 id = 1;
    string name = 2;
    string emails = 3;
}


 通过使用包,可以在一个大型的Protobuf项目中组织消息类型,避免不同消息类型之间的命名冲突。同时,包还可以在生成的代码中生成对应的命名空间,以便在编程语言中进行访问和引用。


选项


 java_package:单独为java定义包名字。


 java_outer_classname:单独为java定义,protobuf编译器生成的类名。


syntax = "proto3";
package com.example.mypackage;
option java_package = "com.example.mypackage";
option java_outer_classname = "Email";
message Email{
    int32 id = 1;
    string name = 2;
    string emails = 3;
}


字段类型


607adc99d8d52e03284da3937ce9a4d.png


枚举、数组、Map


  如下代码:


syntax = "proto3";
message Staff {
    int32 id = 1;
    string name = 2;
    string email = 3;
    // 枚举示例
    enum PhoneType {
        MOBILE = 0;
        TELEPHONE = 1;
    }
    // 嵌套示例
    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
    }
    // list示例
    // 只要使用repeated标记类型定义,就表示数组类型。
    repeated PhoneNumber phone = 4;
    message Map {
        string key = 1;
        int32 value = 2;
    }
    // map示例
    Map map = 5;
}


消息嵌套的几种写法


  1、引用写法


// 定义Result消息
message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3; // 字符串数组类型
}
// 定义SearchResponse消息
message SearchResponse {
  // 引用上面定义的Result消息类型,作为results字段的类型
  repeated Result results = 1; // repeated关键词标记,说明results字段是一个数组
}


  2、嵌套写法


message SearchResponse {
  // 嵌套消息定义
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  // 引用嵌套的消息定义
  repeated Result results = 1;
}


  3、import写法


  在开发一个项目的时候通常有很多消息定义,都写在一个proto文件,不方便维护,通常会将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。


   result.proto


syntax = "proto3";
// Result消息定义
message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3; // 字符串数组类型
}


  search_response.proto


syntax = "proto3";
// 导入Result消息定义
import "result.proto";
// 定义SearchResponse消息
message SearchResponse {
  // 使用导入的Result消息
  repeated Result results = 1; 
}


消息嵌套的调用


  注意在调用消息时,要先调用嵌套消息:


  消息定义:


syntax = "proto3";
message Staff {
    int32 id = 1;
    string name = 2;
    string email = 3;
    // 枚举示例
    enum PhoneType {
        MOBILE = 0;
        TELEPHONE = 1;
    }
    // 嵌套示例
    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
    }
    // list示例
    // 只要使用repeated标记类型定义,就表示数组类型。
    repeated PhoneNumber phone = 4;
    message Map {
        string key = 1;
        int32 value = 2;
    }
    // map示例
    Map map = 5;
}


  调用写法:


public static void main(String[] args) {
    //  序列化
    // 创建Result的Builder
    Staffbuf.Staff.Builder staffBuilder = Staffbuf.Staff.newBuilder();
    staffBuilder.setId(1);
    staffBuilder.setName("张三丰");
    //staffBuilder.setEmail("zhangsanfeng@wudang.org");
    // 构建引用消息(import message)PhoneNumber
    List list = new ArrayList();
    PhoneNumberBuf.PhoneNumber.Builder phoneBuilder =
                      PhoneNumberBuf.PhoneNumber.newBuilder();
    phoneBuilder.setType(PhoneNumberBuf.PhoneNumber.PhoneType.TELEPHONE);
    phoneBuilder.setNumber("010-12345678");
    PhoneNumberBuf.PhoneNumber phoneNumber = phoneBuilder.build();
    list.add(phoneNumber);
    phoneBuilder.clear();
    phoneBuilder.setType(PhoneNumberBuf.PhoneNumber.PhoneType.MOBILE);
    phoneBuilder.setNumber("13912345678");
    list.add(phoneBuilder.build());
    staffBuilder.addAllPhone(list);
    // 完成staff的构建
    Staffbuf.Staff zhangsanfeng = staffBuilder.build();
    // 序列化,byte[]可以被写到磁盘文件,或者通过网络发送出去。
    byte[] data = zhangsanfeng.toByteArray();
    System.out.println("serialization end.");
    // 反序列化,byte[]可以读文件或者读取网络数据构建。
    System.out.println("deserialization begin.");
    try {
        Staffbuf.Staff staff = Staffbuf.Staff.parseFrom(data);
        System.out.println(staff.getId());
        System.out.println(staff.getName());
        staff.getPhoneList().forEach(x -> System.out.println(x.toString()));
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
    }
}


相关文章
|
6月前
|
缓存 人工智能 监控
Prompt Caching终极指南:Claude Code省钱核心+阿里云OpenClaw部署与缓存配置实战教程
在AI编码与智能体开发飞速发展的2026年,成本控制与响应速度成为核心痛点。而Claude Code之所以能实现“低价高效”,其底层核心基础设施——Prompt Caching(提示词缓存)功不可没。这项从设计之初就融入产品架构的技术,能让API调用成本降低90%、响应速度提升85%,彻底改变了AI工具的使用经济性。
3918 1
|
7月前
|
JSON Java 编译器
Protobuf 是什么?一篇文章搞懂这个高性能序列化神器
Protobuf是Google开源的高效二进制序列化协议,体积小、速度快,支持跨语言、向后兼容。相比JSON,更适合RPC等高性能场景,广泛应用于微服务通信。通过`.proto`文件定义结构,自动生成代码,实现数据的快速序列化与反序列化。
2984 158
|
7月前
|
存储 NoSQL 安全
如何保存并分析Linux内核转储(coredump)文件
在Linux中,生成coredump需配置系统参数并满足程序条件。通过ulimit或limits.conf设置核心文件大小,修改core_pattern定义存储路径与命名格式,确保程序无信号屏蔽、权限限制,并留足磁盘空间,最后用gdb分析崩溃堆栈,便于调试定位问题。
|
存储 数据可视化 安全
万字长文读懂低代码(Low-Code)前世今生、分类、能力、趋势及选型
低代码是一种可视化应用开发方式,通过拖拽、配置等方式快速构建系统,降低开发门槛,提升效率。它起源于上世纪80年代,发展至今已广泛应用于ERP、CRM、MES等企业系统。低代码平台具备可视化编程、预置组件、快速部署、多端支持等功能,适合不同规模企业的多样化需求。相比传统开发,其优势在于缩短开发周期、促进业务与技术融合。
|
存储 NoSQL Linux
linux之core文件如何查看和调试
通过设置和生成 core 文件,可以在程序崩溃时获取详细的调试信息。结合 GDB 等调试工具,可以深入分析 core 文件,找到程序崩溃的具体原因,并进行相应的修复。掌握这些调试技巧,对于提高程序的稳定性和可靠性具有重要意义。
8330 6
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
2766 2
|
Ubuntu Linux Docker
弃用Docker Desktop:在WSL2中玩转Docker之Docker Engine 部署与WSL入门
弃用Docker Desktop:在WSL2中玩转Docker之Docker Engine 部署与WSL入门
27504 4
|
自然语言处理 前端开发 JavaScript
V8引擎的运行原理
【9月更文挑战第1天】V8引擎的运行原理
685 0