代码仓库1(实现grpc四种通信模式和拦截器的demo):代码仓库1
代码仓库2(在grpc中实现客户端用户核验,使用JWT):代码仓库2
一、gRPC的基本用法
1.什么是RPC
在学习gRPC之前,我们首先要了解什么是RPC。
RPC(Remote Procedure Call)是远程过程调用协议,它是一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标,RPC框架需提供一种透明调用机制,让使用者不必显式的区分本地调用和远程调用。
通俗地讲,使用RPC进行通信,调用远程函数就像调用本地函数一样,RPC底层会做好数据的序列化与传输,从而能使我们更轻松地创建分布式应用和服务。
2.什么是gRPC
gRPC是一种用于实现RPC的新式的、开源的高性能框架。gRPC源自Google,他基于定义服务的思想,指定可以远程调用的接口及其参数和返回类型。服务端实现这个接口并运行一个 gRPC 服务器来处理客户端调用。而客户端有一个stub(存根,在某些语言中称为客户端client),它提供与服务器相同的方法。客户端通过调用stub的方法来与服务端进行通信,获取响应结果。
gRPC的典型特征就是使用protobuf(全称protocol buffers)作为其接口定义语言(Interface Definition Language,缩写IDL),同时底层的消息交换格式也是使用protobuf。开发人员使用IDL为每个微服务定义服务协定。该协定作为基于文本的 .proto 文件实现,描述了每个服务的方法、输入和输出。同一文件可用于基于不同开发平台和不同语言构建的gRPC客户端和服务。
3.gRPC的四种通信模式
gRPC中服务端和客户端的交互存在着四种不同的通信模式:
- 一元RPC(unary RPC)
- 服务端流 RPC(server-streaming RPC)
- 客户端流 RPC(client-streaming RPC)
- 双向流 RPC(bidirectional-streaming RPC)
下面我们将通过一个小的demo来了解一下这四种通信模式(对应代码仓库1的内容)
3.1 准备工作
我的整体项目结构如下
grpc-communicationMode
│
├───bidirectional-streamingRPC-client//双向流RPC客户端
│ ├───pom.xml
│ ├───src
├───bidirectional-streamingRPC-server//双向流RPC服务端
│ ├───pom.xml
│ ├───src
├───client-streamingRPC-client//客户端流RPC客户端
│ ├───pom.xml
│ ├───src
├───client-streamingRPC-server//客户端流RPC服务端
│ ├───pom.xml
│ ├───src
├───server-streamingRPC-client//服务端流RPC客户端
│ ├───pom.xml
│ ├───src
├───server-streamingRPC-server//服务端流RPC服务端
│ ├───pom.xml
│ ├───src
├───src//主项目源码
├───UnaryRPC-client//一元RPC客户端,内部实现了客户端拦截器
│ ├───pom.xml
│ ├───src
├───UnaryRPC-server//一元RPC服务端,内部实现了服务端拦截器
│ ├───pom.xml
│ ├───src
└───pom.xml//主项目配置文件
首先在idea中创建maven项目 grpc-communicationMode ,作为父项目,引入项目依赖和所需插件
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
</dependencies>
<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>
<protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.51.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
默认情况下,gRPC 使用 Protocol Buffers,这是 Google 提供的一个成熟的开源的跨平台的序列化数据结构的协议,我们编写对应的 proto 文件,通过上面这个插件可以将我们编写的 proto 文件自动转为对应的 Java 类。
接着在src/main/proto 的位置,我们新建一个 book.proto 文件,用于定义数据结构和服务接口。
数据结构即消息类型,我们可以在.proto文件中定义各种消息类型,每种消息类型都可以包含多个字段,每个字段都有一个名称和类型。这些数据结构会在客户端和服务端之间进行交换。
服务接口则定义了服务端可以提供哪些RPC方法,以及这些方法的输入和输出参数。
一旦定义了数据结构和服务接口,就可以使用gRPC的工具来自动生成客户端和服务器的代码。这些代码可以在gRPC支持的任何语言中运行,包括但不限于C++, Java, Python, Go, Ruby,等。
book.proto内容如下,主要定义了一些图书相关的方法:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "ustc.codemaker.grpc.demo1";
option java_outer_classname = "BookServiceProto";
import "google/protobuf/wrappers.proto";
package book;
service BookService {
rpc addBook(Book) returns (google.protobuf.StringValue);//添加图书,使用图书对象插入,返回插入图书的id
rpc getBook(google.protobuf.StringValue) returns (Book);//获取图书,使用id查找,返回图书对象
rpc searchBooks(google.protobuf.StringValue) returns (stream Book);//查找图书,使用tag查找,返回所有包含tag的图书对象
rpc updateBooks(stream Book) returns (google.protobuf.StringValue);//更新图书,给出一系列更新操作,返回所有被更改的图书id
rpc processBooks(stream google.protobuf.StringValue) returns (stream BookSet);//模拟双向流,没有实际意义
}
message Book {
string id = 1;
repeated string tags = 2;
string name = 3;
float price = 4;
string author = 5;
}
message BookSet {
string id = 1;
repeated Book bookList = 3;
}
注意如果不在src/main/proto这个默认位置创建.proto文件的话,那么在配置插件的时候需要指定 proto 文件的位置。
.proto文件是grpc中比较核心的配置,我们简单介绍一下:
- option java_multiple_files = true;可选字段,如果设置为 true,表示每一个 message 文件都会对应一个单独的 class 文件;否则,message 将被全部定义在 outerclass 文件里。
- option java_package = "ustc.codemaker.grpc.ddemo1"; 可选字段,用于标识生成的 java 文件的 package。如果没有指定,则使用 proto 里定义的 package,如果package 也没有指定,那就会生成在根目录下。
- option java_outer_classname = "BookServiceProto"; 可选字段,用于指定 proto 文件生成的 java 类的 outerclass 类名。outerclass是一个包含所有message对应的Java类的class文件。如果没有指定,那么outerclass的名称将默认为proto文件的驼峰式名称。
- package book;这个属性用于定义message的包名。包名的含