Protobuf - 语法、字段使用规则、注意事项

目录

前言

一、Protobuf 基本语法

1.1、Protoc 版本 

1.2、文件格式配置

1.3、消息字段规则

1.3.1、字段数据类型

1.3.2、字段修饰规则

1.3.3、消息类型定义

1.3.4、enum 类型

1.3.5、Any 类型

1.3.6、oneof 类型

1.3.7、map 类型

1.3.8、默认值

1.3.9、更新消息规则

1.3.10、保留字段 reserved

1.3.11、选项 optional(了解,proto3 移除)


前言


前面在讲 gRPC 的时候有讲到 Protobuf 的语法,但实际上远没有这么简单,有很多坑和注意事项,所以这篇文章就是来补坑的~

一、Protobuf 基本语法


1.1、Protoc 版本 

1.2、文件格式配置

a)创建文件:文件都是 proto 为后缀,例如 UserService.proto

b)基本内容:

// 设定使用的 proto 版本
syntax = "proto3";/**java_multiple_files = true 表示 Protobuf 编译器会为每个定义的消息类型生成一个单独的 Java 文件,而不是都放在一个文件中java_multiple_files = false 表示 Protobuf 编译器会把所有的消息类型生成的 Java 代码都放在一个文件中*/
option java_multiple_files = false;/**指定 protobuf 生成的类,放在哪个包中*/
option java_package = "org.cyk";/**指定 protobuf 生成的外部类的名字外部类是用来管理内部类的内部类才是开发中使用的*/
option java_outer_classname = "HelloProto";

c)导包:例如有 A.proto 和 B.proto 文件,现在需要在 B.proto 文件中引入 A.proto 文件的内容,就需要使用  import

import xxx/A.proto

1.3、消息字段规则

1.3.1、字段数据类型

消息中定义的数据类型(我们主要关心 .proto 对应的 Java/Kotlin 类型).

以下列表来自官网:Language Guide (proto 3) | Protocol Buffers Documentation

.proto TypeC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypeRuby TypeC# TypePHP TypeDart Type
doubledoubledoublefloatfloat64Floatdoublefloatdouble
floatfloatfloatfloatfloat32Floatfloatfloatdouble
int32int32intintint32Fixnum or Bignum (as required)intintegerint
int64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
uint32uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
uint64uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sint32int32intintint32Fixnum or Bignum (as required)intintegerint
sint64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
fixed32uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
fixed64uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sfixed32int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
stringstringStringstr/unicode[5]stringString (UTF-8)stringstringString
bytesstringByteStringstr (Python 2)
bytes (Python 3)
[]byteString (ASCII-8BIT)ByteStringstring

1.3.2、字段修饰规则

消息字段可以使用如下规则修饰:

  • singular(proto3 中默认使用此规则):表示该字段只能 null 或者是一个具体值.
  • repeated:表示为 Java 中的 List 类型.

例如如下:

syntax = "proto3";option java_multiple_files = false;
option java_package = "org.cyk";
option java_outer_classname = "UserProto";message Userinfo {string name = 1; // 这里的数字是字段的唯一标识,因为将来时面向字节流传输,需要让每个字段能够对应的上int32 age = 2;repeated string phone = 3;
}

1.3.3、消息类型定义

在单个 .proto 文件中可以定义多个消息,并且支持嵌套类型,不同消息体重的编码可以重复.

syntax = "proto3";option java_multiple_files = false;
option java_package = "org.cyk";
option java_outer_classname = "UserProto";//1.非嵌套
message Userinfo1 {string name = 1;int32 age = 2;repeated string phone = 3;
}//2.嵌套
message Userinfo2 {string name = 1;int32 age = 2;repeated string phone = 3;message Stat {int32 rank = 1;int32 fans = 2;}
}//3.消息类型可作为字段使用
message Human {string aaa = 1;Userinfo1 userinfo = 2;
}

1.3.4、enum 类型

enum ArticlePubType {NORMAL = 0; //发布普通文章PRIVATE = 1; //发布私有文章TIMER = 2; //定时发布文章
}

规则如下:

  • 第一个枚举值必须是 0.
  • 枚举类型可以定义在消息外,也可以在定义在消息体内(嵌套).
  • 枚举的常量值在 32 位整数范围内.  但因为负值无效,所以不建议使用(和编码规则有关).
  • 同级(同一个文件下,或者是引入的其他 proto 文件)枚举类型,枚举值不能重名.

1.3.5、Any 类型

可以简单的理解位 泛型.   因此 Any 中可以存储任意消息类型.  并且 Any 类型也可以使用 repeated 修饰.

Note:Any 类型是 google 已经定义好的类型,在 include 目录下就可以找到所有 google 已经定义好的 .proto 文件.

import "google/protobuf/any.proto"; //引入 Anymessage Userinfo1 {string name = 1;int32 age = 2;repeated string phone = 3;google.protobuf.Any data = 4;
}

将来通过 protoc 编译生成的 Java 文件中,给 Any 类型生成的对象提供了如下方法:

  • hasXXX:用来检测当前字段是否被设置(这个方法存在的意义在于,字段即使不设置也是有默认值的,因此 has 就可以检测到底是否真的有设置值).
  • setXXX:要求传一个 Any 类型的对象.
  • 对于 Any 类型:
    • Any.pack(T message) 可以讲任意消息类型转化成 Any 类型.
    • message.getAny().unpack(Class<T> clazz) 方法可以将 Any 类型转回之前设置的任意消息类型.
    • message.getAny().is(Class<T> clazz) 用来判断存放的消息类型.

1.3.6、oneof 类型

如果消息中有很多可选字段,并且将来只有一个字段会被设置,那么就可以使用 oneof 来约束这个行为.

syntax = "proto3";option java_multiple_files = false;
option java_package = "org.cyk";
option java_outer_classname = "UserProto";import "google/protobuf/any.proto"; //引入 Anymessage Userinfo1 {string name = 1;int32 age = 2;repeated string phone = 3;google.protobuf.Any data = 4;oneof other_content {string qq = 5;string wechat = 6;}
}

注意事项:

  • 可选字段中的 字段编号 不能和 非可选字段 的编号冲突.
  • oneof 中不能使用 repeated 字段.
  • 如果将来 oneof 中有多个字段被设置了值,那么只会保留最后一个设置的成员,之前设置的 oneof 成员会自动清除.

oneof 将来生成 Java 代码中会提供以下方法:

  • clear():清空 oneof 中的字段.
  • getXXXCase():获取当前设置了哪些字段.
  • hasXXX():检查当前字段是否被设置.

1.3.7、map 类型

类似于 Java 中的 HashMap,使用方式如下:

map<key_type>, value_type> map_field = N;

注意:

  • key_type 是除了 float 和 bytes 类型以外的任意标量类型.
  • value_type 可以是任意类型.
  • map 字段不可以用 repeated 修饰.

例如:

syntax = "proto3";option java_multiple_files = false;
option java_package = "org.cyk";
option java_outer_classname = "UserProto";import "google/protobuf/any.proto"; //引入 Anymessage Userinfo1 {string name = 1;int32 age = 2;repeated string phone = 3;google.protobuf.Any data = 4;oneof other_content {string qq = 5;string wechat = 6;}map<string, string> arguments = 7;
}

1.3.8、默认值

如果将来给服务端发送的消息对象中有一些字段没有设置值,那么将来这些消息字段在反序列化时就会被设置默认值.   不同类型默认值不同:

类型默认值
字符串空字符串
字节空字节
布尔值false
数值类型0
枚举默认是第一个定义的枚举值,也就是 0(也必须是 0).
消息字段具体根据语言而定
repeated 修饰的字段空列表
消息字段、oneof字段、any字段有 has 方法来检测当前字段是否被设置

1.3.9、更新消息规则

当现有消息类型已经不再满足我们的需求,需要扩展一个字段的时候,要注意遵守以下规则:

a)禁止修改任何已有的字段编号.

例如你更新的这个 proto 文件中一个已有的字段编号,并且这个新版的 proto 文件被用于生成序列化代码,但是服务端这边反序列化代码还是使用的旧代码进行反序列化,这就可能导致数据丢失或者解析错误.

b)如果要删除老字段,要保证不再使用删除字段的编号.  正确的做法是通过 reserved 保留字段编号,确保该编号不能重复使用.

因为如果我们只是简单的删除了某一个字段而不采取其他措施,那么就可能导致和 (a) 一样的情况.

c)int32、uint32、int64、bool 之间是完全兼容的.  也就是说这些类型中任意一个改成另一个,都不会破坏兼容性.  不过还是要注意,如果从精度较高的字段转化为精度较低的字段可能会被截断.

例如 64位 当作 32位 读取,虽然不存在兼容性问题,但会截断 32 位.

d)新增一个字段到 oneof 类型是不安全的.

这个本质上 和 (a) 问题一致.   因为 oneof 如果被客户端设置的字段是新增的字段,而服务端这边还是使用旧的反序列化代码解析,就可能会出现问题.

1.3.10、保留字段 reserved

如果通过删除字段来更新消息,未来用户在添加新字段时,可能会使用以前被删除的字段编号.  将来使用 proto 旧版本的程序就会引发很多问题.

那么为了确保不会发生这种情况的方法就是:使用 reserverd 将指定字段的 编号 或 名称 设置位保留项.  当我们再使用这些 编号 或 名称 时,protocol 编译器将会警告这些编号不可用.

1.3.11、选项 optional(了解,proto3 移除)

在 proto2 中 optional 是一个字段修饰符,标识字段在消息中是可选的(消息中可能包含该字段,也可能不包含). 但是从 proto3 开始,optional 就被移除了,并且所有字段都是可选的(因为他们都有默认值). 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/15503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css设置文字在固定宽度中等距分开(仅限于单行文本)

一、要实现的效果&#xff1a; 二、代码 要在CSS中设置文本在一个固定宽度的容器中等距分开&#xff0c; 可以使用text-align: justify;属性&#xff0c;它可以让文本两端对齐&#xff0c;看起来就像是等距分开的。 但是要注意&#xff0c;单独使用text-align:justify;只能对单…

【Qt】Qt多元素控件深入解析与实战应用:列表(QListWidget)、表格(QTableWidget)与树形(QTreeWidget)结构

文章目录 前言&#xff1a;Qt中多元素控件&#xff1a;1. List Widget1.1. 代码示例: 使用 ListWidget 2.Table Widget2.1. 代码示例: 使用 QTableWidget 3. Tree Widget3.1. 代码示例: 使用 QTreeWidget 总结&#xff1a; 前言&#xff1a; 在Qt框架中&#xff0c;用户界面的…

C语言内存函数超详细讲解

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C语言内存函数超详细讲解 收录于专栏【C语言学习】 本专栏旨在分享学习C语言学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. m…

C++面向对象程序设计-北京大学-郭炜【课程笔记(十一)】

C面向对象程序设计-北京大学-郭炜【课程笔记&#xff08;十一&#xff09;】 1、string&#xff08;重要知识点&#xff09;1.2、string的赋值和链接1.3、比较string1.4、子串1.5、交换string1.6、寻找string中的字符1.7、删除string中的字符1.8、替换string中的字符1.9、在str…

结构体;结构成员访问操作符

结构体&#xff1a; 虽然c语言已经提供了内置类型&#xff0c;比如&#xff1a;char、short、int、long等&#xff0c;但还是不够用&#xff0c;就好比我描述一个人&#xff0c;我需要描述他的身高&#xff0c;体重&#xff0c;年龄&#xff0c;名字等信息&#xff0c…

微软密谋超级AI大模型!LangChain带你轻松玩转大模型开发

此前&#xff0c;据相关媒体报道&#xff0c;微软正在研发一款名为MAI-1的最新AI大模型&#xff0c;其参数规模或将达5000亿以上&#xff0c;远超此前微软推出的相关开源模型&#xff0c;其性能或能与谷歌的Gemini 1.5、Anthropic的Claude 3和OpenAI的GPT-4等知名大模型相匹敌。…

Linux文本处理三剑客(详解)

一、文本三剑客是什么&#xff1f; 1. 对于接触过Linux操作系统的人来说&#xff0c;应该都听过说Linux中的文本三剑客吧&#xff0c;即awk、grep、sed&#xff0c;也是必须要掌握的Linux命令之一&#xff0c;三者都是用来处理文本的&#xff0c;但侧重点各不相同&#xff0c;a…

Sam Altman微软Build 2024最新演讲:AI可能是下一个移动互联网

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【C++11】lambda匿名函数和包装器

目录 一&#xff0c;lambda匿名函数 1-1&#xff0c;lambda的引入 1-2&#xff0c;lambda表达式书写格式 1-3&#xff0c;lambda函数的名称 1-4&#xff0c;lambda捕获列表的使用 1-5&#xff0c;函数对象与lambda表达式 二&#xff0c;包装器 2-1&#xff0c;function…

如何使用maven运行SpringBoot程序?

目录 一、什么是maven 二、什么是SpringBoot 三、如何使用maven运行SpringBoot程序&#xff1f; 一、什么是maven Maven&#xff1a;简化Java项目构建的自动化工具 在软件开发的世界里&#xff0c;Maven以其强大的项目管理和构建自动化功能&#xff0c;为Java开发者提供了…

容器化:ES和Kibana

1 缘起 最近在学习使用ES&#xff0c; 为了找一个功能强大的可视化工具&#xff0c;之前使用了ES-Head&#xff0c;可以满足学习需求。 闲暇时间又折腾了另一个工具Kibana&#xff0c; 分享如下。 Kibana优点&#xff1a; 用户友好性&#xff1a;Kibana提供直观易用的用户界面…

Strategy设计模式

Strategy设计模式举例。 看图&#xff1a; 代码实现&#xff1a; #include <iostream>using namespace std;class FlyBehavior { public:virtual void fly() 0; };class QuackBehavior { public:virtual void quack() 0; };class FlyWithWings :public FlyBehavior …

数据库(vb.net+OleDB+Access)简易学生信息管理系统

在我们日常生活当中&#xff0c;数据库一词往往离不开我们的编程界&#xff0c;在学校、仓库等方面起着存储数据及数据关系作用的文件。相较于Excel&#xff0c;Access可以存储无限多的记录&#xff0c;内容也十分丰富&#xff0c;例如文本、数字、日期、T&F等。而且不需要…

k8s命令式对象管理和配置

kubectl补全: # dnf install -y bash-completion # echo "source <(kubectl completion bash)" >> ~/.bashrc # kubectl completion bash > /etc/bash_completion.d/kubectl 命令式对象管理 kubectl命令 # 查看所有pod kubectl get pod # 查看某个po…

LLM——探索大语言模型在心理学方面的应用研究

1. 概述 心理学经历了多次理论变革&#xff0c;目前人工智能&#xff08;AI&#xff09;和机器学习&#xff0c;特别是大型语言模型&#xff08;LLMs&#xff09;的使用&#xff0c;预示着新研究方向的开启。本文详细探讨了像ChatGPT这样的LLMs如何转变心理学研究。它讨论了LL…

docker- 镜像 导出导入

文章目录 前言docker- 镜像 导出导入1. 导出2. 删除镜像3. 导入镜像 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

忘记“也是一门学问:机器如何忘记自己学到的知识?

在信息时代&#xff0c;我们常常希望人工智能能够学到更多的知识&#xff0c;变得更加智能。但你是否想过&#xff0c;有时候让机器"忘记"一些它学到的东西&#xff0c;也是一件很重要的事&#xff1f; 随着用户隐私保护意识的提高和相关法律法规的出台&#xff0c;…

深入理解内联函数(C语言)

目录 1.什么是内联函数2.内联函数与宏3.编译器对内联函数的处理4.参考文献 1.什么是内联函数 很多人都会知道&#xff0c;可以将比较小的函数写成内联函数的形式&#xff0c;这样会节省函数调用的开销&#xff0c;具体是什么样的开销呢&#xff1f; 一个函数在执行过程中&…

IDEA通过tomcat运行注意事项

配置run--》edit configurations 以下的A B部分要保持一致 A和B的路径要保持一致

前端vue项目遇到的问题01——那些初级问题

前端vue项目遇到的问题01——那些初级问题 1. npm install 问题1.1 依赖冲突1.1.1 详细问题1.1.2 报错原因1.1.3 解决问题1.1.3.1 方式1——无视冲突1.1.3.1 方式2——更换依赖版本 1.2 nodejs版本问题1.3 node版本正确的情况&#xff08;audit问题&#xff09;&#xff08;这个…