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.xdnf.cn/news/1474705.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue学习笔记之组件传值

说起组件传值&#xff0c;首先要介绍再vue中什么是组件。 组件&#xff08;Component&#xff09;&#xff0c;是vue中很强大的一个功能&#xff0c;可以将一些可重用的代码进行重用。所有的vue组件同时也是vue实例&#xff0c;可以接受使用相同的选项对象和提供相同的生命周期…

高考志愿填报千万要注意这四点

在高考志愿填报过程中&#xff0c;确实有很多需要留心的点。我为你总结了四个关键点&#xff0c;希望能帮助你顺利完成志愿填报&#xff1a; 1、学校提供的支持 学校作为学生志愿填报咨询服务的主阵地&#xff0c;应提供体系化和制度化的支持。包括及时关注并传达政策动向和相…

Spring AOP源码篇三之 xml配置

简单代码示例, 了解Spring AOP基于xml的基本用法 xml配置&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-insta…

数学系C++ 排序算法简述(八)

目录 排序 选择排序 O(n2) 不稳定&#xff1a;48429 归并排序 O(n log n) 稳定 插入排序 O(n2) 堆排序 O(n log n) 希尔排序 O(n log2 n) 图书馆排序 O(n log n) 冒泡排序 O(n2) 优化&#xff1a; 基数排序 O(n k) 快速排序 O(n log n)【分治】 不稳定 桶排序 O(n…

Linux—网络设置

目录 一、ifconfig——查看网络配置 1、查看网络接口信息 1.1、查看所有网络接口 1.2、查看具体的网络接口 2、修改网络配置 3、添加网络接口 4、禁用/激活网卡 二、hostname——查看主机名称 1、查看主机名称 2、临时修改主机名称 3、永久修改主机名称 4、查看本…

Polkadot(DOT)即将爆雷?治理无能还歧视亚洲!资金将在两年内耗尽!是下一个FTX吗?

近期&#xff0c;关于Polkadot(DOT)生态圈的一系列负面消息引发了业界和投资者的广泛关注。从高昂的营销开支、缺乏实际业务亮点&#xff0c;再到治理问题和种族歧视指控&#xff0c;Polkadot似乎正面临着严峻的危机。业内人士警告&#xff0c;Polkadot的财政状况堪忧&#xff…

一个最简单的comsol斜坡稳定性分析例子——详细步骤

一个最简单的comsol斜坡稳定性分析例子——详细步骤 标准模型例子—详细步骤 线弹性模型下的地应力平衡预应力与预应变、土壤塑性和安全系数求解的辅助扫描

Vue2前端实现数据可视化大屏全局自适应 Vue实现所有页面自适应 Vue实现自适应所有屏幕

Vue自适应所有屏幕大小,目前页面自适应,尤其是数据可视化大屏的自适应更是案例很多 今天就记录一下使用Vue全局自适应各种屏幕大小的功能 在Vue.js中创建一个数据大屏,并使其能够自适应不同屏幕大小,通常涉及到布局的响应式设计、CSS媒体查询、以及利用Vue的事件系统来处理…

非同步升压转换器,效率95%你信吗?ETA1611输出电流2A, 22V DCDC

前言&#xff1a; 截止24年7月7日某创报价&#xff1a;500&#xff1a; &#xffe5;0.7856 / 个 建议使用前同时了解下方器件。 2毛钱的SOT23-5封装28V、1.5A、1.2MHz DCDC转换器用于LCD偏置电源和白光LED驱动等MT3540升压芯片 描述 ETA1611 SOT23-6封装 丝印GVYW&#xff0…

对话大模型Prompt是否需要礼貌点?

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 基于Dify的QA数据集构建&#xff08;附代码&#xff09;Qwen-2-7B和GLM-4-9B&#x…

YOLOv8结合SAHI推理图像和视频

文章目录 前言视频效果必要环境一、完整代码二、运行方法1、 推理图像2、 推理视频 总结 前言 在上一篇文章中&#xff0c;我们深入探讨了如何通过结合YOLOv8和SAHI来增强小目标检测效果 &#xff0c;并计算了相关评估指标&#xff0c;虽然我们也展示了可视化功能&#xff0c;…

入门PHP就来我这(高级)13 ~ 图书添加功能

有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 今天给大家接着上篇文章编写图书添加功能。 1 添加页面 创建add.html页面样式&#xff0c;废…

去O化神器 Exbase

随着去O化进程推动&#xff0c;很多旧业务依赖的oracle数据库&#xff0c;都需要实现做数据库的替换&#xff0c;当下能很好兼容Oracle&#xff0c;并实现异构数据库之间转换的工具并不多。这里给大家推荐一个商业工具数据库迁移工具exbase&#xff08;北京海量&#xff09;&am…

排序格式排序格式

排序格式排序格式

CosyVoice - 阿里最新开源语音克隆、文本转语音项目 支持情感控制及粤语 本地一键整合包下载

近日&#xff0c;阿里通义实验室发布开源语音大模型项目FunAudioLLM&#xff0c;而且一次包含两个模型&#xff1a;SenseVoice和CosyVoice。 CosyVoice专注自然语音生成&#xff0c;支持多语言、音色和情感控制&#xff0c;支持中英日粤韩5种语言的生成&#xff0c;效果显著优于…

Java多线程不会?一文解决——

方法一 新建类如MyThread继承Thread类重写run()方法再通过new MyThread类来新建线程通过start方法启动新线程 案例&#xff1a; class MyThread extends Thread {public MyThread(String name) {super(name);}Overridepublic void run() {for(int i0;i<10;i){System.out.…

深度学习中的Channel,通道数是什么?

参考文章&#xff1a; 直观理解深度学习的卷积操作&#xff0c;超赞&#xff01;-CSDN博客​​​​​​如何理解卷积神经网络中的通道&#xff08;channel&#xff09;_神经网络通道数-CSDN博客 深度学习-卷积神经网络—卷积操作详细介绍_深度卷积的作用-CSDN博客 正文&…

土豆炒肉做法

菜单&#xff1a;土豆、葱、铁辣子、纯瘦肉、淀粉、生抽、酱油、刀、案板、十三香、盐巴、擦板 流程&#xff1a; 洗土豆&#xff0c;削皮&#xff0c;擦成条&#xff0c;用凉水过滤两遍淀粉&#xff0c;顺便放个燥里洗肉&#xff0c;切成条&#xff0c;按照生抽、酱油、淀粉、…

QT入门笔记-自定义控件封装 30

具体代码如下: QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES QT_DISABLE_DEPRECATED_BEFORE0x060000 …

vue3项目 前端blocked:mixed-content问题解决方案

一、问题分析 blocked:mixed-content其实浏览器不允许在https页面里嵌入http的请求&#xff0c;现在高版本的浏览器为了用户体验&#xff0c;都不会弹窗报错&#xff0c;只会在控制台上打印一条错误信息。一般出现这个问题就是在https协议里嵌入了http请求&#xff0c;解决方法…