SpringBoot基于gRPC进行RPC调用

SpringBoot基于gRPC进行RPC调用

  • 一、gRPC
    • 1.1 什么是gRPC?
    • 1.2 如何编写proto
    • 1.3 数据类型及对应关系
    • 1.4 枚举
    • 1.5 数组
    • 1.6 map类型
    • 1.7 嵌套对象
  • 二、SpringBoot gRPC
    • 2.1 工程目录
    • 2.2 jrpc-api
      • 2.2.1 引入gRPC依赖
      • 2.2.2 编写 .proto 文件
      • 2.2.3 使用插件机制生产proto相关文件
    • 2.2 jrpc-server
      • 2.2.1 引入 `jrpc-api` 依赖
      • 2.2.2 编写impl
      • 2.2.3 编写Config
      • 2.2.4 yaml
    • 2.3 jrpc-client
      • 2.3.1 引入 `jrpc-api` 依赖
      • 2.3.2 编写config
      • 2.3.3 yaml
      • 2.3.4 测试验证

一、gRPC

1.1 什么是gRPC?

In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object, making it easier for you to create distributed applications and services. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a gRPC server to handle client calls. On the client side, the client has a stub (referred to as just a client in some languages) that provides the same methods as the server.

在 gRPC 中,客户端应用程序可以直接调用服务器应用程序上的方法 在另一台机器上,就好像它是本地对象一样,使你更容易 创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 是 基于定义服务的思想,指定可以 使用其参数和返回类型进行远程调用。在服务器端, server 实现此接口并运行 gRPC 服务器来处理客户端调用。 在客户端,客户端有一个存根(在某些客户端中称为客户端 languages),它提供与服务器相同的方法。
在这里插入图片描述

gRPC 客户端和服务器可以在各种 环境 - 从 Google 内部的服务器到您自己的桌面 - 并且可以 用 gRPC 支持的任何语言编写。因此,例如,您可以轻松地 在 Java 中创建一个 gRPC 服务器,客户端使用 Go、Python 或 Ruby。另外 最新的 Google API 将具有其接口的 gRPC 版本,让您 轻松将 Google 功能构建到您的应用程序中。

gRPC 使用 proto buffers 作为服务定义语言,编写 proto 文件,即可完成服务的定义

在这里插入图片描述

1.2 如何编写proto

syntax = "proto3";option java_multiple_files = true;
// 生成位置
option java_package = "com.lizq.jrpc.api";
option java_outer_classname = "UserService";package user;service User {rpc SayHello (UserRequest) returns (UserResponse) {}
}message UserRequest {string name = 1;int32 age = 2;string addr = 3;
}message UserResponse {string name = 1;int32 age = 2;string addr = 3;OtherMsg otherMsg = 4;map<string, string> otherMap = 5;// 嵌套对象message OtherMsg {string ext1 = 1;string ext2 = 2;}
}
  • syntax = "proto3";:指定使用的protobuf版本;
  • option java_multiple_files = true;:如果为 false,则只会.java为此文件生成一个.proto文件,以及所有 Java 类/枚举/等。为顶级消息、服务和枚举生成的将嵌套在外部类中。如果为 true,.java将为每个 Java 类/枚举/等生成单独的文件。为顶级消息、服务和枚举生成,并且为此.proto文件生成的包装 Java 类将不包含任何嵌套类/枚举/等。 如果不生成 Java 代码,则此选项无效。
  • package user;:定义本服务的包名,避免不同服务相同消息类型产生冲突;
  • option java_package = "com.lizq.jrpc.api";:生成java文件包名;
  • option java_outer_classname = "UserService";:生成java文件类名称。如果文件中没有明确 java_outer_classname指定,.proto则将通过将.proto文件名转换为驼峰式来构造类名(因此 foo_bar.proto变为FooBar.java)
  • message UserResponse:定义服务的接口名称;
  • rpc SayHello (UserRequest) returns (UserResponse) {}:远程调用方法名,参数及响应类型;
  • message XXXXX{}:定义数据类型;

1.3 数据类型及对应关系

.proto类型NotesC++ TypeJava/KotlinPython
doubledoubledoublefloat
floatfloatfloatfloat
int32使用可变长度编码。对负数进行编码效率低下——如果您的字段可能有负值,请改用 sint32。int32intint
int64使用可变长度编码。对负数进行编码效率低下——如果您的字段可能有负值,请改用 sint64。int64longint/long
uint32使用可变长度编码。uint32intint/long
uint64使用可变长度编码。uint64longint/long
sint32使用可变长度编码。带符号的 int 值。这些比常规 int32 更有效地编码负数。int32intint
sint64使用可变长度编码。带符号的 int 值。这些比常规 int64 更有效地编码负数。int64longint/long
fixed32总是四个字节。如果值通常大于 228,则比 uint32 更有效uint32intint/long
fixed64总是八个字节。如果值通常大于 256,则比 uint64 更有效。uint64longint/long
sfixed32总是四个字节。int32intint
sfixed64总是八个字节。int64longint/long
boolboolbooleanbool
string字符串必须始终包含 UTF-8 编码或 7 位 ASCII 文本,并且不能超过 232。stringStringstr/unicode
bytes可能包含不超过 2 32的任意字节序列。stringByteStringstr (Python 2)、bytes (Python 3)

1.4 枚举

enum Sex {NONE = 0;MAN = 1;WOMAN = 2;
}message UserRequest {string name = 1;int32 age = 2;string addr = 3;Sex sex = 4;
}

**注意:**第一个枚举的值必须为0,因为0 是默认值,0 必须是第一个,保持和proto2 兼容

1.5 数组

使用 repeated 关键字来定义数组。

message UserRequest {string name = 1;int32 age = 2;string addr = 3;Sex sex = 4;// 定义一个数组repeated string cellphones = 5;
}

1.6 map类型

在开发的过程中经常需要使用关联字段,很自然的想到使用map,protobuf也提供了map的类型。

message UserResponse {string name = 1;map<string, string> otherMap = 2;
}

注意: map 字段前面不能是repeated

1.7 嵌套对象

message UserResponse {string name = 1;int32 age = 2;string addr = 3;OtherMsg otherMsg = 4;map<string, string> otherMap = 5;// 嵌套对象message OtherMsg {string ext1 = 1;string ext2 = 2;}
}

二、SpringBoot gRPC

2.1 工程目录

在这里插入图片描述

2.2 jrpc-api

2.2.1 引入gRPC依赖

<dependency><groupId>io.grpc</groupId><artifactId>grpc-all</artifactId><version>1.28.1</version>
</dependency>

2.2.2 编写 .proto 文件

syntax = "proto3";option java_multiple_files = true;
// 生成位置
option java_package = "com.lizq.jrpc.api";
option java_outer_classname = "UserService";package user;service User {rpc SayHello (UserRequest) returns (UserResponse) {}
}message UserRequest {string name = 1;int32 age = 2;string addr = 3;
}message UserResponse {string name = 1;int32 age = 2;string addr = 3;OtherMsg otherMsg = 4;map<string, string> otherMap = 5;// 嵌套对象message OtherMsg {string ext1 = 1;string ext2 = 2;}
}

2.2.3 使用插件机制生产proto相关文件

在 jrpc-api pom.xml 中添加如下:

<build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.6.2</version></extension></extensions><plugins><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><!--&lt;!&ndash; ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成--><protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.28.1:exe:${os.detected.classifier}</pluginArtifact><!--protoSourceRoot 默认src/main/proto--><protoSourceRoot>src/main/proto</protoSourceRoot></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins>
</build>

执行命令生产proto相关文件

在这里插入图片描述
将生成的文件拷贝到工程中,如下:

在这里插入图片描述

2.2 jrpc-server

jrpc-server 为 springboot 项目。

2.2.1 引入 jrpc-api 依赖

<dependency><groupId>com.example</groupId><artifactId>jrpc-api</artifactId><version>1.0.0-SNAPSHOT</version>
</dependency>

2.2.2 编写impl

@Service
public class UserServiceImpl extends UserGrpc.UserImplBase {@Overridepublic void sayHello(UserRequest request, StreamObserver<UserResponse> responseObserver) {Map<String, String> otherMap = new HashMap<>();otherMap.put("test", "testmap");UserResponse response = UserResponse.newBuilder().setName("server:" + request.getName()).setAddr("server:" + request.getAddr()).setAge(request.getAge()).setOtherMsg(UserResponse.OtherMsg.newBuilder().setExt1("ext1").setExt2("ext2").build()).putAllOtherMap(otherMap).build();responseObserver.onNext(response);responseObserver.onCompleted();}
}

2.2.3 编写Config

@Configuration
public class GrpcServerConfiguration {@Value("${grpc.server-port}")private int port;@Beanpublic Server server() throws Exception {System.out.println("Starting gRPC on port {}." + port);// 构建服务端ServerBuilder<?> serverBuilder = ServerBuilder.forPort(port);// 添加需要暴露的接口this.addService(serverBuilder);// startServer server = serverBuilder.build().start();System.out.println("gRPC server started, listening on {}." + port);// 添加服务端关闭的逻辑Runtime.getRuntime().addShutdownHook(new Thread(() -> {System.out.println("Shutting down gRPC server.");if (server != null) {// 关闭服务端server.shutdown();}System.out.println("gRPC server shut down successfully.");}));if (server != null) {// 服务端启动后直到应用关闭都处于阻塞状态,方便接收请求server.awaitTermination();}return server;}@Autowiredprivate UserServiceImpl userService;/*** 添加需要暴露的接口* @param serverBuilder*/private void addService(ServerBuilder<?> serverBuilder) {serverBuilder.addService(userService);}
}

2.2.4 yaml

server:port: 8081
spring:application:name: spring-boot-jrpc-server
grpc:server-port: 18081

2.3 jrpc-client

2.3.1 引入 jrpc-api 依赖

<dependency><groupId>com.example</groupId><artifactId>jrpc-api</artifactId><version>1.0.0-SNAPSHOT</version>
</dependency>

2.3.2 编写config

@Configuration
public class GrpcClientConfiguration {@Value("${server-host}")private String host;/*** gRPC Server的端口*/@Value("${server-port}")private int port;@Beanpublic ManagedChannel managedChannel() {// 开启gRPC客户端ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();System.out.println("gRPC client started, server address: " + host + " , " + port);// 添加客户端关闭的逻辑Runtime.getRuntime().addShutdownHook(new Thread(() -> {try {// 调用shutdown方法后等待1秒关闭channelmanagedChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);System.out.println("gRPC client shut down successfully.");} catch (InterruptedException e) {e.printStackTrace();}}));return managedChannel;}@Autowiredprivate ManagedChannel managedChannel;@Beanpublic UserGrpc.UserBlockingStub userBlockingStub(ManagedChannel channel) {// 通过channel获取到服务端的stubreturn UserGrpc.newBlockingStub(managedChannel);}
}

2.3.3 yaml

server:port: 8080
spring:application:name: spring-boot-jrpc-client# 本地测试
server-host: 127.0.0.1
server-port: 18081

2.3.4 测试验证

@RestController("/user")
public class UserController {@Autowiredprivate UserGrpc.UserBlockingStub userBlockingStub;@GetMapping("/sayHello")public String sayHello(String name, String addr, int age) {UserRequest request = UserRequest.newBuilder().setName(name).setAddr(addr).setAge(age).build();UserResponse response;try {response = userBlockingStub.sayHello(request);} catch (StatusRuntimeException e) {e.printStackTrace();return e.getMessage();}return response.toString();}
}

浏览器访问:http://localhost:8080/user/sayHello?name=test&addr=addr&age=99 返回:

name: "server:test" age: 99 addr: "server:addr" otherMsg { ext1: "ext1" ext2: "ext2" } otherMap { key: "test" value: "testmap" }

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

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

相关文章

大语言模型加速信创软件 IDE 技术革新

QCon 全球软件开发大会&#xff08;上海站&#xff09;将于 12 月 28-29 日举办&#xff0c;会议特别策划「智能化信创软件 IDE」专题&#xff0c;邀请到华为云开发工具和效率领域首席专家、华为软件开发生产线 CodeArts 首席技术总监王亚伟担任专题出品人&#xff0c;为专题质…

2018年第七届数学建模国际赛小美赛B题世界杯足球赛的赛制安排解题全过程文档及程序

2018年第七届数学建模国际赛小美赛 B题 世界杯足球赛的赛制安排 原题再现&#xff1a; 有32支球队参加国际足联世界杯决赛阶段的比赛。但从2026年开始&#xff0c;球队的数量将增加到48支。由于时间有限&#xff0c;一支球队不能打太多比赛。因此&#xff0c;国际足联提议改变…

机器人也能干的更好:RPA技术的优势和应用场景

RPA是什么&#xff1f; 机器人流程自动化RPA&#xff08;Robotic Process Automation&#xff09;是一种自动化技术&#xff0c;它使用软件机器人来高效完成重复且有逻辑性的工作。近年来&#xff0c;随着人工智能和自动化技术的不断发展和普及&#xff0c;RPA已经成为企业提高…

【Linux】Linux运维基础

Linux简介&#xff1a; Linux是一个开源的操作系统内核&#xff0c;最初由Linus Torvalds创建。它通常与GNU工具一起使用&#xff0c;以创建一个完整的操作系统。Linux操作系统有许多基于内核的发行版&#xff0c;如Ubuntu、CentOS、Debian等&#xff0c;每个发行版都有其独特的…

DC-5靶场

目录 DC-5靶机&#xff1a; 先进行主机发现&#xff1a; 发现文件包含&#xff1a; 上传一句话木马&#xff1a; 反弹shell&#xff1a; 提权漏洞利用&#xff1a; 下载exp&#xff1a; 第一个文件 libhax.c 第二个文件r…

C++面向对象(OOP)编程-异常机制

本文主要介绍C异常的处理&#xff0c;异常的种类&#xff0c;以及如何自己实现异常&#xff0c;以及异常常见的面试题。 目录 1 异常介绍 2 异常处理 2.1 抛出异常 2.2 捕获异常 2.3 自定义异常 3 异常使用原则 4 异常处理模式 5 stdexcept 中的异常类 6 异常机制面试…

window10下载与安装zookeeper,图文说明

1&#xff0c;下载 打开连接 &#xff1b;https://downloads.apache.org/zookeeper/ 选择版本下载 2&#xff0c;解压 cmd黑窗口解压命令 tar -zxvf apache-zookeeper-3.8.3-bin3&#xff0c;修改配置 复制zoo_sample.cfg&#xff0c;重命名为zoo.cfg zoo.cfg配置 # The …

最新AI创作系统ChatGPT系统源码+DALL-E3文生图+支持AI绘画+GPT语音对话功能

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

Vue 项目中使用 debugger 在 chrome 谷歌浏览器中失效以及 console.log 指向去了 vue.js 代码

问题 今天在代码里面输出 console.log 信息直接指向了 vue.js&#xff0c;并且代码里面写了 debgger 也不生效 解决 f12 找到浏览器的这个设置图标 找到这个 ignore list 的 custom exclusion rules 取消掉 /node_modules/|/bower_components/ 这样就正常了

Linux--学习记录(3)

G重要编译参数 -g&#xff08;GDB调试&#xff09; -g选项告诉gcc产生能被GNU调试器GDB使用的调试信息&#xff0c;以调试程序编译带调试信息的可执行文件g -g hello.c -o hello编译过程&#xff1a; -E&#xff08;预处理&#xff09; g -E hello.c -o hello.i-S&#xff08;编…

PHPStorm一站式配置

phpstorm安装好之后&#xff0c;先别急着编码。工欲善其事&#xff0c;必先利其器&#xff0c;配置好下面这些之后让编码事半功倍。 主题 Appearance & Behavior -> Appearance -> Theme 选中 [Light with Light Header] 亮色较为护眼 关闭更新 Appearance & …

数据分析思维导图

参考&#xff1a; https://zhuanlan.zhihu.com/p/567761684?utm_id0 1、数据分析步骤地图 2、数据分析基础知识地图 3、数据分析技术知识地图 4、数据分析业务流程 5、数据分析师能力体系 6、数据分析思路体系 7、电商数据分析核心主题 8、数据科学技能书知识地图 9、数据挖掘…