GRPC使用之ProtoBuf

1. 入门指导

1. 基本定义

Protocol Buffers提供一种跨语言的结构化数据的序列化能力,类似于JSON,不过更小、更快,除此以外它还能用用接口定义(IDL interface define language),通protoc编译Protocol Buffer定义文件,生成结构化类,以及服务调用的客户端和服务端。

1. person.proto

我们来看一个极简的例子,好让自己有一个直观的感受,假设我们有一个person.proto文件,内容如下:

syntax = "proto3";option java_multiple_files = true;
option java_package = "org.keyniu.grpc.proto";message Person {optional string name = 1;optional int32 id = 2;optional string email = 3;
}
2. Person.java

通过protoc生成的Person.java类大概是这样的

// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: person.proto// Protobuf Java Version: 3.25.1
package org.keyniu.grpc.proto;/*** Protobuf type {@code Person}*/
public final class Person extendscom.google.protobuf.GeneratedMessageV3 implements// @@protoc_insertion_point(message_implements:Person)PersonOrBuilder {
private static final long serialVersionUID = 0L;// Use Person.newBuilder() to construct.private Person(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {super(builder);}private Person() {name_ = "";email_ = "";}
... // 后续的省略
3. 核心用例

我们看一下Person类的核心用法

// 创建对象
Person p = Person.newBuilder().setName("randy").setId(1).setEmail("randy@gmail.com").build();
// 序列化
byte[] serialized = p.toByteArray();
// 反序列化
Person p2 = Person.parseFrom(serialized);
System.out.println(p2);
2. 适用场景

Protocol Buffers提供了结构化数据的序列化/反序列化能力,对领域对象的修改能够兼容历史版本,官方推荐适用于小规模数据(MB级),包括网络传输、数据存储。

不适用的场景包括

  1. 不支持流式解析,待解析的数据要一次性加载进byte数组,然后解析,不能读取部分内容就交由Protocol Buffers解析
  2. 不支持二进制比较,不同语言/平台的序列化后的二进制可能是不同的,要反序列化后才能比较两个对象是否相同
  3. 不支持非面向对象的语言

2. 数据类型

我们先来回顾一下person.proto的定义,这个定义的核心是Person前面的message

message Person {optional string name = 1; // label(optional)、字段类型(string)、字段名(name)、字段Id(1)optional int32 id = 2;optional string email = 3;
}
1. label
label说明举例
optional字段是否可选,允许不设置值,proto3中字段默认optional,对应proto2中的requiredoptional string name = 1;
repeated可以有0或多个值,保留写入顺序repeated string name = 1;
map对应Java里的Mapmap<int32, string> idToName = 2;
字段是否存在,被称为implicit field presence,如果字段未设置值,序列化
oneof一组关联字段,只保留一个值,设置两个字段时,会把第一个清空

来看一个oneof的实例,一个Product对象,它可以参加一种促销(抵用券或打折),但不能同时参加,可以这样定义product.proto

syntax = "proto3";
option java_multiple_files = true;
option java_package = "org.keyniu.grpc.proto";message Product {optional string name = 1;oneof promotion {string coupon = 2;string discount = 3;}
}

我们来看看生成的Product类,Product类自动生成了一个Product.PromotionCase类,我们可以通过它判断当前Product参加那类促销

Product prod = Product.newBuilder().setName("Mate60Pro").setCoupon("满10减3").build();
System.out.print(prod.toString());
switch (prod.getPromotionCase()) {case DISCOUNT:System.out.println(prod.getDiscount());break;case COUPON:System.out.println(prod.getCoupon());break;
}

输出如下
在这里插入图片描述

如果我们给Product同时设置Coupon和Discount,代码如下:

prod = Product.newBuilder().setName("Mate60Pro").setCoupon("满100减1").setDiscount("7折").build();
System.out.println(prod.toString());

输出如下
在这里插入图片描述

2. 字段类型

类型分为内置基本类型和自己通过message(enum)定义的类型,我们先来看看基本类型。 proto3的内置基本类型,包括整数、浮点数、布尔型、字符串以及字节数组

1. 基本类型
Proto类型对应Java类型说明默认值
doubledouble0
floatfloat0
int32int变长编码,对负数的编码效率较低,如果有负数建议使用sint320
int64long变长编码,对负数的编码效率较低,如果有负数建议使用sint640
uint32int变长编码,相当于unsigned int320
uint64long变长编码,相当于unsigned int640
sint32int变长编码,对负数的编码效率较高0
sint64long变长编码,对负数的编码效率较高0
fixed32int定长编码,总是使用4 Byte,占空间多,但编码效率高,相当于unsigned int320
sfixed32int定长编码,总是使用4 Byte,相当于signed int320
fixed64long定长编码,总是使用8 Byte,占空间多,但编码效率高,相当于unsigned int640
sfixed64long定长编码,总是使用8 Byte,相当于signed int640
boolboolean布尔值false
stringString字符串空字符串
bytesByteString字节序列,适用于存储任何数据,比如图片的byte数字空字节数组
2. 自定义类型

除此以外,proto允许用户自己通过message、enum定义自己的类型,比如之前提到的Person,我们再看一下示例

message Person {optional string name = 1;optional int32 id = 2;optional string email = 3;
}
3. 自定义枚举

enum关键字和Java的枚举基本一致,假设我们要定义一个性别(Gender)的枚举,可以用下面的语句定义

enum Gender {MALE = 0;FEMALE = 1;
}

要特别注意的是枚举字段的定义后面的字段id要从0开始。此外enum还有一个Java没有有的特性,枚举类型可以指定别名,比如将MAN作为MALE的别名可以这么写

enum Gender {option allow_alias = true;MALE = 0;MAN = 0;FEMALE = 1;WOMEN = 1;
}
4. 跨文件引用

如果通过message、enum定义的类型都在同一个文件中,可以直接相互引用,如果是在两个proto文件中,需要手动import,比如这样

import "myproject/gender.proto";
3. 字段ID

官方叫做Assigned Field Number,在person.proto中name字段的id就是1,id在同一个类型内部必须唯一,取值范围[1,5亿],一般从1开始递增,当然数字越大,消耗的储空间越大(类似UTF-8编码)。19000~19999是预留给内部使用的。

optional string name = 1;

所以这个字段标识不能修改,也不能重复,修改导致之前序列化的数据无法解析,重复导致字段混乱。

4. 预留字段

告诉Protocol Buffer预留字段Id ,2、9、10、11、15,预留字段名称foo、bar

message Foo {reserved 2, 15, 9 to 11;reserved "foo", "bar";
}
5. 对象引用

某些场景下我们可能不确定持有的数据类型,比如Object,proto也提供了这样的支持

import "google/protobuf/any.proto";message Handler {string message = 1;repeated google.protobuf.Any target = 2;
}

通过生成对象的pack、unpack方法来访问target持有的引用

class Any {// Packs the given message into an Any using the default type URL// prefix “type.googleapis.com”.public static Any pack(Message message);// Packs the given message into an Any using the given type URLpublic static Any pack(Message message, String typeUrlPrefix);// Checks whether this Any message’s payload is the given type.public <T extends Message> boolean is(class<T> clazz);// Unpacks Any into the given message type. Throws exception if// the type doesn’t match or parsing the payload has failed.public <T extends Message> T unpack(class<T> clazz) throws InvalidProtocolBufferException;
}

3. 服务定义

proto3支持4中类型的服务定义,通过service关键字类定义服务的接口,比如下面示例中的Greeter服务,定义了4个方法,分别对应4种类型的调用

syntax = "proto3";option java_multiple_files = true;
option java_package = "org.keyniu.grpc.generate";service Greeter {rpc sayHello (HelloRequest) returns (HelloReply) {}rpc sayHelloClientStream (stream HelloRequest) returns (HelloReply) {}rpc sayHelloServerStream (HelloRequest) returns (stream HelloReply) {}rpc sayHelloBiStream (stream HelloRequest) returns (stream HelloReply) {}
}
1. 基本调用

基本调用,处理入参HelloRequest,生成响应HelloReply,在Java中怎么实现可以参考[[Helloworld#2. 实现Server]]。

rpc sayHello (HelloRequest) returns (HelloReply) {}
2. Client端Streaming

Server端Streaming,指客户端可能提交多个参数,最后响应一个结果

rpc sayHelloClientStream (stream HelloRequest) returns (HelloReply) {}
3. Server端Streaming

Server端Streaming,指客户端提供一个参数,服务端可能会有多个响应

rpc sayHelloServerStream (HelloRequest) returns (stream HelloReply) {}
4. 双向Streaming

双向Streaming,指客户端可以提交多个参数,服务端也可以有多个响应

rpc sayHelloBiStream (stream HelloRequest) returns (stream HelloReply) {}

4. JSON互操作

我们可能需要让ProtoBuff和JSON交互,ProtoBuff也为我们考虑到了这个问题,在Java中,可以使用protobuf-java-util实现这个能力

<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java-util</artifactId><version>3.x.x</version> <!-- 使用你的protobuf版本 -->
</dependency>
1. 转JSON
import com.google.protobuf.util.JsonFormat;
import your.package.YourProtoMessage; // 替换为你的protobuf消息类型public class Main {public static void main(String[] args) throws Exception {YourProtoMessage message = YourProtoMessage.newBuilder() // 构建你的protobuf消息.setField1("value1") // 设置字段.setField2(123)       // 设置字段.build();JsonFormat.Printer printer = JsonFormat.printer();String jsonString = printer.print(message);System.out.println(jsonString);}
}
2. 解析JSON
import com.google.protobuf.util.JsonFormat;
import your.package.YourProtoMessage; // 替换为你的protobuf消息类型public class Main {public static void main(String[] args) throws Exception {String jsonString = "{\"field1\":\"value1\",\"field2\":123}";JsonFormat.Parser parser = JsonFormat.parser();YourProtoMessage message = parser.merge(jsonString, YourProtoMessage.newBuilder()).build();System.out.println(message.getField1()); // 输出: value1System.out.println(message.getField2()); // 输出: 123}
}

5. 配置选项

选项示例说明
option java_packageoption java_package = “com.example.foo”生成Java类的包名
java_outer_classnameoption java_outer_classname = “Person”;生成Java类外部包装类名称
java_multiple_filesoption java_multiple_files = true;一个proto文件message、service生成多.java文件
optimize_foroption optimize_for = CODE_SIZE;可选值SPEED、CODE_SIZE、LITE_RUNTIME
SPEED: 追求执行速度,生成序列化/反序列化等代码
CODE_SIZE: 追求代码体积,通过反射实现序列化
LITE_RUNTIME: 类似SPEED,但省略descriptor和reflection代码,依赖libprotobuf-lite

A. 参考资料

  1. https://protobuf.dev/programming-guides/proto3/

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

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

相关文章

Android14系统应用统一裁剪方案

Android14系统应用统一裁剪方案 背景 当前移除集成到系统里的应用,一般都是根据应用名,到各个mk文件里逐个在PRODUCT_PACKAGES中删除;这种方法,耗时而且不易管理集成到系统里的应用;需要有一个统一管理删除不需要应用的方案。 方案 参考PRODUCT_PACKAGES变量,添加PRO…

淘宝商品历史价格查询(免费)

当前资料来源于网络&#xff0c;禁止用于商用&#xff0c;仅限于学习。 淘宝联盟里面就可以看到历史价格 并且没有加密 淘宝商品历史价格查询可以通过以下步骤进行&#xff1a; 先下载后&#xff0c;登录app注册账户 打开淘宝网站或淘宝手机App。在搜索框中输入你想要查询的商…

短视频矩阵系统多账号搭建技术源码(saas开发者技术独立搭建)

在构建云服务环境以部署虚拟机方面&#xff0c;以Amazon Web Services&#xff08;AWS&#xff09;为示例&#xff0c;需采购并配置适当数量的EC2实例以及相关网络设施。 接下来&#xff0c;根据业务需求&#xff0c;应创建多个社交媒体平台如抖音和快手的官方账户&#xff0c;…

【计算机毕业设计】基于Springboot的B2B平台医疗病历交互系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

【记录】LaTex|LaTex 代码片段 Listings 添加带圆圈数字标号的箭头(又名 LaTex Tikz 库画箭头的简要介绍)

文章目录 前言注意事项1 Tikz 的调用方法&#xff1a;newcommand2 标号圆圈数字的添加方式&#xff1a;\large{\textcircled{\small{1}}}\normalsize3 快速掌握 Tikz 箭头写法&#xff1a;插入点相对位移标号node3.1 第一张图&#xff1a;插入点相对位移3.2 第二张图&#xff1…

如何玩转澳大利亚Facebook直播?

近年来&#xff0c;直播带货已经成为国内最赚钱的行业之一&#xff0c;各种玩法也越来越成熟。然而&#xff0c;在海外市场&#xff0c;尤其是澳大利亚&#xff0c;直播带货仍然是一片蓝海。作为社交媒体营销的主阵地&#xff0c;Facebook的直播功能却常常被卖家忽视。那么&…

贴脸细看Mixtral 8x7B- 稀疏混合专家模型(MoE)的创新与推动

贴脸细看Mixtral 8x7B- 稀疏混合专家模型&#xff08;MoE&#xff09;的创新与推动 原创 一路到底孟子敬 上堵吟 2024年01月15日 20:05 美国 I. 引言 A. Mixtral 8x7B的背景和目的 • 背景&#xff1a;随着大型语言模型在自然语言处理&#xff08;NLP&#xff09;领域的广泛…

【java计算机毕设】基于java的奶茶店管理系统的设计与实现MySQL ssm JSP maven项目代码源码+文档

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】奶茶店管理系统java MySQL ssm JSP maven项目源码代码万字文档 小组设计 2项目介绍 系统功能&#xff1a; 奶茶店管理系统包括管理员、用户俩种角色。 管理员功能包括个人中心模块用于修改个人信息和密码、…

游戏AI的创造思路-技术基础-情感计算(1)

游戏中的AI也是可以和你打情感牌的哦&#xff0c;不要以为NPC是没有感情的&#xff0c;不过&#xff0c;不要和NPC打过多的情感牌&#xff0c;你会深陷其中无法自拔的~~~~~~ 目录 1. 情感计算算法定义 2. 发展历史 3. 公式和函数 3.1. 特征提取阶段 TF-IDF&#xff08;词频…

AJAX-个人版2.0

AJAX&#xff08;Asynchronous Javascript And Xml&#xff09; 传统请求及缺点 传统的请求都有哪些&#xff1f; 直接在浏览器地址栏上输入URL。点击超链接提交form表单使用JS代码发送请求 window.open(url)document.location.href urlwindow.location.href url… 传统请…

一键换衣,这个AI可以让你实现穿衣自由

基于图像的虚拟穿衣是一种流行且前景广阔的图像合成技术&#xff0c;能够显著改善消费者的购物体验&#xff0c;并降低服装商家的广告成本。顾名思义&#xff0c;虚拟穿衣任务旨在生成目标人穿着给定服装的图像。 OOTDiffusion简述 图1 虚拟换衣 基于图像的虚拟穿衣目前面临两…

系统分析师-基础知识

基础知识 一、计算机组成与结构1、计算机系统基础知识1.1 计算机硬件组成1.2 中央处理单元&#xff08;CPU&#xff09;1.3 数据表示1.3.1 R进制转十进制&#xff1a;1.3.2 十进制转R进制&#xff1a; 1.4 校验码&#xff08;3种校验码&#xff09;1.4.1 基本知识1.4.2 奇偶校验…

2024-07抖音/快手/小红书/视频号/美团无人直播技术:最新不封号无人直播的操作方法详细介绍

2024年最新研究出来的无人直播技术&#xff0c;目前不封号&#xff0c;用途大大的&#xff0c;可带货&#xff0c;可引流&#xff0c;可获客。 手机自动直播源码通常涉及到实时流媒体技术和应用开发&#xff0c;它涉及以下几个关键部分&#xff1a; 摄像头接入&#xff1a;使用…

vue3 学习笔记01 -- 搭建项目及基础配置

vue3 学习笔记01 – 搭建项目及基础配置 确保你已经安装了Node.js&#xff08;建议使用最新的LTS版本&#xff09; 搭建项目 初始化项目 如果选择npm创建项目再执行 npm create vitelatest my-vue3-app --template vue-ts使用yarn&#xff0c;如果电脑没有安装yarn cnpm i ya…

(一)、python程序--模拟电脑鼠走迷宫

一、绪论 1、简介 电脑鼠走迷宫是一种比赛&#xff0c;制作实物电脑鼠小车在迷宫找目标点&#xff0c;用时最短者获胜。考验参赛选手软硬件结合的能力。 2、走迷宫模拟软件中已实现功能 1、点击迷宫墙壁可编辑迷宫&#xff0c;并且可保存和加载迷宫形状文件&#xff1b; 2、…

无线麦克风哪个牌子的好,麦克风哪个品牌音质最好,热门款式推荐

​在当今这个数字化迅速发展的时代&#xff0c;无线领夹麦克风已经成为自媒体创作者、直播主播以及专业录音师不可或缺的工具。它们以其小巧便携、易于操作和出色的录音质量&#xff0c;极大地提升了音频录制的效率和质量。无论是户外探险的Vlog拍摄&#xff0c;还是室内直播的…

基于Android平台开发,天气预报APP

1.项目功能思维导图 2. 项目涉及到的技术点 数据来源&#xff1a;和风天气API使用okhttp网络请求框架获取api数据使用gson库解析json数据使用RecyclerViewadapter实现未来7天列表展示和天气指数使用PopupMenu 实现弹出选项框使用动画定时器实现欢迎页倒计时和logo动画使用Text…

解决IDEA每次新建项目都需要重新配置maven的问题

每次打开IDEA都要重新配置maven&#xff0c;这是因为在DEA中分为项目设置和全局设置&#xff0c;这个时候我们就需要去到全局中设置maven了。我用的是IntelliJ IDEA 2023.3.4 (Ultimate Edition)&#xff0c;以此为例。 第一步&#xff1a;打开一个空的IDEA&#xff0c;选择左…

传知代码-多行人姿态检测系统

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 本项目创新在于采用多级网络串联工作来进行目标的行为分析&#xff0c;并使用在视频监控领域&#xff0c;可部署在任何有需要的人员流动密集场所(如医院&#xff0c;机场&#xff0c;养老院等)或者用于空巢…

springboot三层架构详细讲解

目录 springBoot三层架构0.简介1.各层架构1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2.6 C…