gRPC
本博客源码地址: github.com/wz20/gprc-l...
一、gRPC 的基本概念
gRPC 简介
gRPC 是 Google 开源的一个高性能的 RPC(Remote Procedure Call) 框架。(由Google内部RPC项目Stubby演化而来,2015年正式开源)
主要使用场景:
- 低延迟、高度可扩展的分布式系统。
- 开发与云服务器通信的移动客户端。
- 设计一个需要准确、高效且独立于语言的新协议。
- 分层设计以实现扩展,例如。身份验证、负载平衡、日志记录和监控等。
核心设计思路:
要完成一个RPC通信的几个要素:
- 网络通信
- 协议
- 序列化
- 代理的创建
- gRPC自己封装了网络通信的部分,提供了多种语言的网络通信封装。(C、Java[本质上使用Netty]、GO.....)
- gRPC使用HTTP2协议。(传输数据的时候使用二进制数据内容、数据传输更快、支持双向流、连接的多路复用)
- gRPC没有使用 XML 或者 JSON 这种文本格式,而是采用了基于 protocol buffers (google开源的一种序列化方式,在dubbo等中也能使用) 的二进制协议。
- gRPC封装了代理stub。让调用者像调用本地方法那样调用远端的服务方法。
gRPC的好处:
- 高效的进程间通信
- 支持多种语言实现,原生支持C、GO、Java。
- 支持多平台运行 Linux、Android、IOS、MacOS、Windows
- 序列化方式采用protobuf,效率更高
- 使用HTTP2协议
- 生态丰富,大厂背书
HTTP2.0 介绍
HTTP1.x 协议
- Http1.0协议 :请求响应的模式,短连接协议(无状态协议),传输数据文本结构,单工(无法实现服务端推送,只能采用客户端轮询的方式变相实现)
- Http1.1协议 :请求响应的模式,有限的长连接(Keepalived)。如果使用webSocket方式可以实现双工,服务器向客户端推送。
总结:HTTP1.x的传输方式为文本格式,可读性更好,但是效率更差。本质上是无法实现双工通信(HTTP1.1本身不能实现)。
HTTP2.0 协议
-
二进制协议,效率高,但可读性差
-
HTTP/2 使用头部压缩来减少传输数据的大小。这有助于减少网络带宽的使用量,并提高页面加载速度。
-
多路复用,只通过一个 TCP连接就可以传输所有的请求数据。多路复用可以绕过浏览器限制同一个域名下的请求数量的问题,进而提高了网页的性能。
-
HTTP/2 允许服务器在不被请求的情况下主动向客户端发送数据。这有助于减少请求延迟,并提高页面加载速度。
HTTP2.0 协议的核心概念:
- 连接 Connection: 1 个 TCP 连接,包含一个或者多个 Stream。
- 数据流 Stream:一个双向通讯数据流,包含 1 条或者多条 Message。
- 消息 Message: 对应 HTTP/1 中的请求或者响应,包含一条或者多条 Frame。
- 数据帧 Frame:最小单位,以二进制压缩格式存放 HTTP/1 中的内容。
Tip:更多HTTP2.0知识可以查看:HTTP/2 协议(帧、消息、流简单的抓包分析)_http2 抓包-CSDN博客
Protocol Buffers
Protocol Buffers是一种与语言无关、与平台无关的可扩展机制,用于序列化结构化数据。
它就像 JSON 一样,除了它是 更小、更快,并且它会生成本地语言绑定。您定义如何 您希望您的数据一次结构化,然后您可以使用特殊生成的 用于轻松编写和读取各种结构化数据的源代码 数据流并使用多种语言。
protobuf 需要安装protobuf的编译器,编译器的目的就是把protobuf的IDL语言,转换成具体某一种开发语言。
Protobuf 环境准备
下载编译器:
Release Protocol Buffers v3.19.6 · protocolbuffers/protobuf (github.com)
安装(win):解压缩并配置环境变量
验证:protoc --version
IDEA插件安装
Protobuf 语法基础
-
文件格式
.proto UserService.proto xxxxx.proto
-
版本设定 (第一行)
inisyntax = "proto3";
-
注释
arduino//单行注释 /* 多行注释 */
-
与Java语言相关的语法
ini//后续protobuf生成的java代码是一个源文件还是多个源文件 option java_multiple_files = false; //一个源文件 //指定protobuf生成的类放置在哪个包中 option java_package = "com.proto"; //protobuf生成java外部类(仅用于管理内部类,内部类才是我们真正使用的)的名称 option java_outer_classname = "ProtoOuter";
-
逻辑包(Java工程师用的较少)
go//protobuf对于文件内容的管理 package xxx;
-
导入
arduinoimport "xxxx/UserService.proto"
Protobuf 核心语法
基本类型: Language Guide (proto 3) | Protocol Buffers Documentation (protobuf.dev)
枚举:
ini
enum SEASON{
SPRING = 0;
SUMMER = 1;
}
//枚举的值 必须是0开始
消息:Message
ini
message LoginRequest{
string username = 1; //username字段的编号
string password = 2;
int32 age = 3;
}
-
编号:从1开始,最大到2^29-1,注意:19000 - 19999 不可用,是protobuf自保留编号。
-
关键字:
- singular : 默认,代表这个字段的值只能包括0个或1个
- repeated : 这个字段的返回值是多个,等价于Java中的List。
-
protobuf 中可以定义多个message
-
message 是可以嵌套的
inimessage LoginRequest{ message User{ string name = 1; string password = 2; } string xxx = 1; int32 yyy = 2; User ppp = 3; } message Result{ LoginRequest.User aaa = 1; }
-
oneof【其中一个】
inimessage SimpleMessage{ // test_oneOf 只能代表name 或 age 其中一个 oneof test_oneOf{ string name = 1; int32 age = 2; } }
Protobuf 服务定义
scss
service HelloService{
//定义若干个服务的方法,定义服务接口
rpc hello(LoginRequest) returns(Result){};
}
- 一个service可以定义多个服务方法。
- 可以定义多个服务接口(service)
- gRPC 服务有四种服务方式
二、gRPC开发实战
show me code, 下面写一个gRPC的hello world程序!!!
第一个gRPC应用
项目结构:
API 模块开发
-
编写 .proto 文件的IDL。
inisyntax = "proto3"; option java_multiple_files = false; option java_package = "com.wz"; option java_outer_classname = "HelloProto"; /** IDL文件目的:发布RPC服务 */ message HelloRequest{ string name = 1; } message HelloResponse{ string result = 1; } service HelloService{ rpc hello(HelloRequest) returns(HelloResponse){} }
-
通过protoc 命令将proto文件中的IDL 转换成编程语言
iniprotoc --java_out = /xxx/xxx/xx.proto
这种方式并不适用真实的开发环境,实战开发中,我们会采用maven的插件来进行编译并把他放置到具体位置。
-
配置maven插件
grpc/grpc-java: The Java gRPC implementation. HTTP/2 based RPC (github.com)
xml <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.58.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.58.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.58.0</version> </dependency> <dependency> <!-- necessary for Java 9+ --> <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.7.1</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.24.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <!-- 生成服务接口--> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
-
执行maven插件
compile执行成功后,可以在target中找到对应的代码:复制到开发目录下即可。
执行compile-custom命令,可以看到对应的代码:复制到开发目录下即可。
优化:我们可以通过自定义输出目录
xml<configuration> <!-- 编译--> <protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <!-- 生成服务接口--> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact> <outputDirectory>${basedir}/src/main/java</outputDirectory> <!-- 追加生成,避免清除以前的代码--> <clearOutputDirectory>false</clearOutputDirectory> </configuration>
至此,api相关开发已完成。
代码简略分析:
Service 模块开发
- 实现业务接口 (添加具体功能)
- 创建服务器 (Netty)
-
引入api
xml<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>rpc-grpc-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
实现业务接口
javapackage com.wz.service; import com.wz.HelloProto; import com.wz.HelloServiceGrpc; import io.grpc.stub.StreamObserver; /** * @ClassName: HelloServiceImpl * @Description: 具体得逻辑 * @Author: Ze WANG * @Date: 2023/10/9 * @Version 1.0 **/ public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { /** * grpc 的返回值并不是通过Java中的返回值来返回。而是通过观察者设计模式通过参数返回的,后续会有详细的解释 * 1. 接收客户端提交的参数 * 2. 业务处理 * 3. 返回处理结果 * @param request req * @param responseObserver 响应观察者 */ @Override public void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) { //1.接收client的请求参数 String name = request.getName(); //2.业务处理 System.out.println("service name===>"+name); //3.封装响应 //3.1 创建响应对象的构造者 HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); //3.2 填充数据 builder.setResult("hello method invoke ok"); //3.3 封装响应对象 HelloProto.HelloResponse helloResponse = builder.build(); //4.返回给客户端 responseObserver.onNext(helloResponse); //5.通知客户端响应已结束 responseObserver.onCompleted(); } }
-
创建服务端
javapackage com.wz.server; import com.wz.service.HelloServiceImpl; import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; /** * @ClassName: GrpcServer1 * @Description: 服务端1 * @Author: Ze WANG * @Date: 2023/10/9 * @Version 1.0 **/ public class GrpcServer1 { public static void main(String[] args) throws InterruptedException, IOException { //1.设置端口 ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000); //2.发布服务 serverBuilder.addService(new HelloServiceImpl()); //3.创建服务对象 Server server = serverBuilder.build(); //4.启动服务器 server.start(); server.awaitTermination(); } }
-
启动服务器
Client 模块开发
- 通过代理对象完成远端对象的调用
java
package com.wz.client;
import com.wz.HelloProto;
import com.wz.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
/**
* @ClassName: GrpcClient1
* @Description: 客户端
* @Author: Ze WANG
* @Date: 2023/10/9
* @Version 1.0
**/
public class GrpcClient1 {
public static void main(String[] args) {
//1.创建通信的管道
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build();
try {
//2.获得代理对象 stub
HelloServiceGrpc.HelloServiceBlockingStub helloServiceBlockingStub = HelloServiceGrpc.newBlockingStub(managedChannel);
//3.完成rpc调用
//3.1 准备参数
HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
builder.setName("WangZe");
HelloProto.HelloRequest helloRequest = builder.build();
//3.2 rpc请求
HelloProto.HelloResponse helloResponse = helloServiceBlockingStub.hello(helloRequest);
//3.3 获取返回值
String result = helloResponse.getResult();
System.out.println("rpc响应内容==》"+result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
managedChannel.shutdown();
}
}
}
package com.wz.client;
import com.wz.HelloProto;
import com.wz.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
/**
* @ClassName: GrpcClient1
* @Description: 客户端
* @Author: Ze WANG
* @Date: 2023/10/9
* @Version 1.0
**/
public class GrpcClient1 {
public static void main(String[] args) {
//1.创建通信的管道
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build();
try {
//2.获得代理对象 stub
HelloServiceGrpc.HelloServiceBlockingStub helloServiceBlockingStub = HelloServiceGrpc.newBlockingStub(managedChannel);
//3.完成rpc调用
//3.1 准备参数
HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
builder.setName("WangZe");
HelloProto.HelloRequest helloRequest = builder.build();
//3.2 rpc请求
HelloProto.HelloResponse helloResponse = helloServiceBlockingStub.hello(helloRequest);
//3.3 记牌器瞎忙活忙活没人品冒号
String result = helloResponse.getResult();
System.out.println("rpc响应内容==》"+result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
managedChannel.shutdown();
}
}
}
启动客户端后,观察客户端的与服务端的打印:
Client:
Service:
注意事项:
gRPC的四种通信方式
四种通信方式:
- 简单RPC(一元RPC) Unary RPC
- 服务端流式RPC Server Streaming RPC
- 客户端流式RPC Client Streaming RPC
- 双向流RPC Bi-directional Stream RPC
简单RPC
我们第一个gRPC应用就是使用的这种RPC,简单RPC的特点是:请求-响应式
当客户端发起调用后,会提交数据并等待服务端响应。
语法:
scss
service HelloService{
rpc hello(HelloRequest) returns(HelloResponse){}
}
服务端流式RPC
概念:一个请求对象,服务端可以回传多个结果对象。多个结果往往不是一起返回的
使用场景: 服务端需要源源不断的给客户端返回数据。
-
交易所的k线图:
lua股票标号 client -----> Server <----- 某一个时刻的股票行情 <----- 另外一个时刻的股票行情
语法:
scss
service HelloService{
rpc hello(HelloRequest) returns(stream HelloResponse){}
}
show me code
-
在proto文件中添加一个service方法,并生成对应的Java代码:
scssservice HelloService{ rpc hello(HelloRequest) returns(HelloResponse){} rpc c2ss(HelloRequest) returns(stream HelloResponse){} }
-
在service中实现业务逻辑
java@Override public void c2ss(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) { //1.接收client的请求参数 String name = request.getName(); //2.业务处理 System.out.println("service param name===>"+name); //3.封装响应 for (int i = 0; i < 10; i++) { try { //3.1 创建响应对象的构造者 HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); //3.2 填充数据 builder.setResult("处理结果==>" + i); //3.3 封装响应对象 HelloProto.HelloResponse helloResponse = builder.build(); //4.返回给客户端 responseObserver.onNext(helloResponse); //模拟时间间隔 Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } //5.通知客户端响应已结束 responseObserver.onCompleted(); }
-
服务发布(因为我们是在HelloServiceImpl中实现的,所以不需要再次发布了)
arduinopublic class GrpcServer1 { public static void main(String[] args) throws InterruptedException, IOException { //1.设置端口 ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000); //2.发布服务 serverBuilder.addService(new HelloServiceImpl()); //3.创建服务对象 Server server = serverBuilder.build(); //4.启动服务器 server.start(); server.awaitTermination(); } }
-
编写客户端
javapackage com.wz.client; import com.wz.HelloProto; import com.wz.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import java.util.Iterator; /** * @ClassName: GrpcClient2 * @Description: 客户端,用于测试服务端流式rpc * @Author: Ze WANG * @Date: 2023/10/9 * @Email: Ze_Wang2@human-horizons.com * @Version 1.0 **/ public class GrpcClient2 { public static void main(String[] args) { //1.创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build(); try { //2.获得代理对象 stub HelloServiceGrpc.HelloServiceBlockingStub helloServiceBlockingStub = HelloServiceGrpc.newBlockingStub(managedChannel); //3.完成rpc调用 //3.1 准备参数 HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("WangZe"); HelloProto.HelloRequest helloRequest = builder.build(); //3.2 rpc请求 Iterator<HelloProto.HelloResponse> helloResponseIterator = helloServiceBlockingStub.c2ss(helloRequest); //3.3 获取返回值 while (helloResponseIterator.hasNext()){ HelloProto.HelloResponse helloResponse = helloResponseIterator.next(); System.out.println("helloResponse.getResult()"+helloResponse.getResult()); } } catch (Exception e) { throw new RuntimeException(e); } finally { managedChannel.shutdown(); } } }
-
启动客户端观察返回:
Tips: 这里我们使用的阻塞(接收所有的message才继续处理)的监听方式,我们实战中客户端不应该一直阻塞,而是应该使用异步监听(来一个message就处理一个)
监听-异步方式 处理服务端流式RPC开发
修改客户端的逻辑:
csharp
public class GrpcClient3 {
/**
* 用于存储响应消息
*/
static List<HelloProto.HelloResponse> responses = new ArrayList<>();
public static void main(String[] args) {
//1.创建通信的管道
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build();
try {
//2.获得代理对象 stub
HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel);
//3.完成rpc调用
//3.1 准备参数
HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
builder.setName("WangZe");
HelloProto.HelloRequest helloRequest = builder.build();
//3.2 rpc请求
helloServiceStub.c2ss(helloRequest, new StreamObserver<HelloProto.HelloResponse>() {
//3.3 响应的监听,根据不同的时间做不同的处理 onNext() onError() onCompleted()
@Override
public void onNext(HelloProto.HelloResponse value) {
//监听消息
System.out.println("服务端发来了一个消息:"+value);
responses.add(value);
}
@Override
public void onError(Throwable t) {
//监听异常
System.out.println("ERROR:"+t.getMessage());
}
@Override
public void onCompleted() {
//把服务端所有的数据拿到后,再进行业务处理,在此处写
System.out.println("所有消息集合:");
responses.forEach(System.out::println);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
managedChannel.shutdown();
System.out.println("client处理结束......");
}
}
}
注意:客户端没有阻塞,所以拿到一个消息后执行完逻辑进程就关闭了。
优化后的逻辑: 对客户端添加等待时间
java
package com.wz.client;
import com.wz.HelloProto;
import com.wz.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.ClientResponseObserver;
import io.grpc.stub.StreamObserver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName: GrpcClient3
* @Description: 异步
* @Author: Ze WANG
* @Date: 2023/10/13
* @Email: Ze_Wang2@human-horizons.com
* @Version 1.0
**/
public class GrpcClient3 {
/**
* 用于存储响应消息
*/
static List<HelloProto.HelloResponse> responses = new ArrayList<>();
public static void main(String[] args) {
//1.创建通信的管道
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build();
try {
//2.获得代理对象 stub
HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel);
//3.完成rpc调用
//3.1 准备参数
HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
builder.setName("WangZe");
HelloProto.HelloRequest helloRequest = builder.build();
//3.2 rpc请求
helloServiceStub.c2ss(helloRequest, new StreamObserver<HelloProto.HelloResponse>() {
//3.3 响应的监听,根据不同的时间做不同的处理 onNext() onError() onCompleted()
@Override
public void onNext(HelloProto.HelloResponse value) {
//监听消息
System.out.println("服务端发来了一个消息:"+value);
responses.add(value);
}
@Override
public void onError(Throwable t) {
//监听异常
System.out.println("ERROR:"+t.getMessage());
}
@Override
public void onCompleted() {
//把服务端所有的数据拿到后,再进行业务处理,在此处写
System.out.println("所有消息集合:");
responses.forEach(System.out::println);
}
});
//设置等待时间
managedChannel.awaitTermination(60, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
managedChannel.shutdown();
System.out.println("client处理结束......");
}
}
}
运行结果:
vbnet
服务端发来了一个消息:result: "Result==>0"
服务端发来了一个消息:result: "Result==>1"
服务端发来了一个消息:result: "Result==>2"
服务端发来了一个消息:result: "Result==>3"
服务端发来了一个消息:result: "Result==>4"
服务端发来了一个消息:result: "Result==>5"
服务端发来了一个消息:result: "Result==>6"
服务端发来了一个消息:result: "Result==>7"
服务端发来了一个消息:result: "Result==>8"
服务端发来了一个消息:result: "Result==>9"
所有消息集合:
result: "Result==>0"
result: "Result==>1"
result: "Result==>2"
result: "Result==>3"
result: "Result==>4"
result: "Result==>5"
result: "Result==>6"
result: "Result==>7"
result: "Result==>8"
result: "Result==>9"
客户端流式RPC
概念:客户端发送多个请求对象,服务端只返回一个结果。
应用场景:IOT(物联网)
例如车上的传感器,源源不断的将车辆信号数据发送到服务端。服务端不需要一直响应给车。
编程语法:protobuf
scss
service HelloService{
rpc cs2s(stream HelloRequest) returns(HelloResponse){}
}
show me Code
-
编写protobuf 并生成API
inisyntax = "proto3"; option java_multiple_files = false; option java_package = "com.wz"; option java_outer_classname = "HelloProto"; /** IDL文件目的:发布RPC服务 */ message HelloRequest{ string name = 1; } message HelloResponse{ string result = 1; } service HelloService{ rpc hello(HelloRequest) returns(HelloResponse){} rpc c2ss(HelloRequest) returns(stream HelloResponse){} rpc cs2s(stream HelloRequest) returns(HelloResponse){} }
-
开发服务端
typescript/** * 客户端流式RPC服务端 * @param responseObserver 返回responseObserver * @return 监控请求消息 StreamObserver */ @Override public StreamObserver<HelloProto.HelloRequest> cs2s(StreamObserver<HelloProto.HelloResponse> responseObserver) { return new StreamObserver<HelloProto.HelloRequest>() { @Override public void onNext(HelloProto.HelloRequest value) { //1.监控每一个client发送的消息 System.out.println("接收到客户端消息:"+value); } @Override public void onError(Throwable t) { //2.监控异常 System.out.println("监听到异常:"+t.getMessage()); } @Override public void onCompleted() { //3.全部消息是否发送完整 System.out.println("客户端消息全部接收成功"); //响应一个全部信息 HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); builder.setResult("all message is ok"); HelloProto.HelloResponse helloResponse = builder.build(); responseObserver.onNext(helloResponse); responseObserver.onCompleted(); } }; }
-
开发客户端:
javapackage com.wz.client; import com.wz.HelloProto; import com.wz.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @ClassName: GrpcClient4 * @Description: 客户端流式RPC 客户端 * @Author: Ze WANG * @Date: 2023/10/13 * @Email: Ze_Wang2@human-horizons.com * @Version 1.0 **/ public class GrpcClient4 { /** * 用于存储响应消息 */ static List<HelloProto.HelloResponse> responses = new ArrayList<>(); public static void main(String[] args) { //1.创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build(); try { //2.获得代理对象 stub HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel); //3.完成rpc调用 StreamObserver<HelloProto.HelloRequest> helloRequestStreamObserver = helloServiceStub.cs2s(new StreamObserver<HelloProto.HelloResponse>() { @Override public void onNext(HelloProto.HelloResponse value) { System.out.println("监听到服务端返回:"+value); } @Override public void onError(Throwable t) { } @Override public void onCompleted() { System.out.println("服务端响应完毕"); } }); for (int i = 0; i < 10; i++) { HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("WangZe"+i); HelloProto.HelloRequest helloRequest = builder.build(); //发送消息 helloRequestStreamObserver.onNext(helloRequest); //睡眠 Thread.sleep(1000); } //发送结束 helloRequestStreamObserver.onCompleted(); managedChannel.awaitTermination(60,TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); } finally { managedChannel.shutdown(); } } }
-
运行结果
lessClient: 已连接到目标 VM, 地址: ''127.0.0.1:56552',传输: '套接字'' 监听到服务端返回:result: "all message is ok" 服务端响应完毕 Server: 接收到客户端消息:name: "WangZe0" 接收到客户端消息:name: "WangZe1" 接收到客户端消息:name: "WangZe2" 接收到客户端消息:name: "WangZe3" 接收到客户端消息:name: "WangZe4" 接收到客户端消息:name: "WangZe5" 接收到客户端消息:name: "WangZe6" 接收到客户端消息:name: "WangZe7" 接收到客户端消息:name: "WangZe8" 接收到客户端消息:name: "WangZe9" 客户端消息全部接收成功
双向流RPC
概念: 客户端可以发送多个请求消息,服务端也可以响应多个消息
应用场景: 聊天室
编码:
scss
rpc cs2ss(stream HelloRequest) returns(stream HelloResponse){}
show me code
-
api
inisyntax = "proto3"; option java_multiple_files = false; option java_package = "com.wz"; option java_outer_classname = "HelloProto"; /** IDL文件目的:发布RPC服务 */ message HelloRequest{ string name = 1; } message HelloResponse{ string result = 1; } service HelloService{ rpc hello(HelloRequest) returns(HelloResponse){} rpc c2ss(HelloRequest) returns(stream HelloResponse){} rpc cs2s(stream HelloRequest) returns(HelloResponse){} rpc cs2ss(stream HelloRequest) returns(stream HelloResponse){} }
-
开发服务端
typescript@Override public StreamObserver<HelloProto.HelloRequest> cs2ss(StreamObserver<HelloProto.HelloResponse> responseObserver) { return new StreamObserver<HelloProto.HelloRequest>() { @Override public void onNext(HelloProto.HelloRequest value) { //1.监控每一个client发送的消息 System.out.println("接收到客户端消息:"+value); //=====每次都返回===== HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); builder.setResult("message is ok"); HelloProto.HelloResponse helloResponse = builder.build(); responseObserver.onNext(helloResponse); } @Override public void onError(Throwable t) { //2.监控异常 System.out.println("监听到异常:"+t.getMessage()); } @Override public void onCompleted() { //3.全部消息是否发送完整 System.out.println("客户端消息全部接收成功"); //响应一个全部信息 HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); builder.setResult("all message is ok"); HelloProto.HelloResponse helloResponse = builder.build(); responseObserver.onNext(helloResponse); responseObserver.onCompleted(); } }; }
-
开发客户端
javapackage com.wz.client; import com.wz.HelloProto; import com.wz.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.concurrent.TimeUnit; /** * @ClassName: GrpcClient5 * @Description: 双向流客户端 * @Author: Ze WANG * @Date: 2023/10/13 * @Email: Ze_Wang2@human-horizons.com * @Version 1.0 **/ public class GrpcClient5 { public static void main(String[] args) { //1.创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build(); try { //2.获得代理对象 stub HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel); //3.完成rpc调用 StreamObserver<HelloProto.HelloRequest> helloRequestStreamObserver = helloServiceStub.cs2ss(new StreamObserver<HelloProto.HelloResponse>() { @Override public void onNext(HelloProto.HelloResponse value) { System.out.println("监听到服务端返回:"+value); } @Override public void onError(Throwable t) { } @Override public void onCompleted() { System.out.println("服务端响应完毕"); } }); for (int i = 0; i < 10; i++) { HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("WangZe"+i); HelloProto.HelloRequest helloRequest = builder.build(); //发送消息 helloRequestStreamObserver.onNext(helloRequest); //睡眠 Thread.sleep(1000); } //发送结束 helloRequestStreamObserver.onCompleted(); managedChannel.awaitTermination(60, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); } finally { managedChannel.shutdown(); } } }
-
运行结果
sqlclient: 已连接到目标 VM, 地址: ''127.0.0.1:57026',传输: '套接字'' 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "message is ok" 监听到服务端返回:result: "all message is ok" 服务端响应完毕 ====================================================== server: 已连接到目标 VM, 地址: ''127.0.0.1:56974',传输: '套接字'' 接收到客户端消息:name: "WangZe0" 接收到客户端消息:name: "WangZe1" 接收到客户端消息:name: "WangZe2" 接收到客户端消息:name: "WangZe3" 接收到客户端消息:name: "WangZe4" 接收到客户端消息:name: "WangZe5" 接收到客户端消息:name: "WangZe6" 接收到客户端消息:name: "WangZe7" 接收到客户端消息:name: "WangZe8" 接收到客户端消息:name: "WangZe9" 客户端消息全部接收成功
gRPC代理方式总结
-
BlockingStub
阻塞 通信方式
-
Stub
异步 通过监听处理
-
FutureStub
同步 异步 NettyFuture
FutureStub只能应用于 简单RPC
FutureStub 案例:
1.首先创建一个新的proto文件,并生成API
ini
syntax = "proto3";
option java_multiple_files = false;
option java_package = "com.wz";
option java_outer_classname = "TestProto";
message TestRequest{
string name = 1;
}
message TestResponse{
string result = 1;
}
service TestService{
rpc testWz(TestRequest) returns(TestResponse){}
}
2.开发服务端Impl
scala
public class TestServiceImpl extends TestServiceGrpc.TestServiceImplBase {
@Override
public void testWz(TestProto.TestRequest request, StreamObserver<TestProto.TestResponse> responseObserver) {
String name = request.getName();
System.out.println("name:"+name);
//响应
responseObserver.onNext(TestProto.TestResponse.newBuilder().setResult("test is ok").build());
responseObserver.onCompleted();
}
}
arduino
public class GrpcServer1 {
public static void main(String[] args) throws InterruptedException, IOException {
//1.设置端口
ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000);
//2.发布服务
serverBuilder.addService(new HelloServiceImpl());
serverBuilder.addService(new TestServiceImpl());
//3.创建服务对象
Server server = serverBuilder.build();
//4.启动服务器
server.start();
server.awaitTermination();
}
}
3.开发客户端
-
同步
javapackage com.wz.client; import com.google.common.util.concurrent.ListenableFuture; import com.wz.HelloProto; import com.wz.HelloServiceGrpc; import com.wz.TestProto; import com.wz.TestServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; /** * @ClassName: GrpcClientFutureStub * @Description: FutureStub * @Author: Ze WANG * @Date: 2023/10/13 * @Email: Ze_Wang2@human-horizons.com * @Version 1.0 **/ public class GrpcClientFutureStub { public static void main(String[] args) { //1.创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build(); try { TestServiceGrpc.TestServiceFutureStub testServiceFutureStub = TestServiceGrpc.newFutureStub(managedChannel); ListenableFuture<TestProto.TestResponse> future = testServiceFutureStub.testWz(TestProto.TestRequest.newBuilder().setName("wangze").build()); //同步 TestProto.TestResponse testResponse = future.get(); System.out.println(testResponse); System.out.println("后续操作..........."); } catch (Exception e) { throw new RuntimeException(e); } finally { managedChannel.shutdown(); } } }
-
异步:
typescriptpublic class GrpcClientFutureStub { public static void main(String[] args) { //1.创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9000).usePlaintext().build(); try { TestServiceGrpc.TestServiceFutureStub testServiceFutureStub = TestServiceGrpc.newFutureStub(managedChannel); ListenableFuture<TestProto.TestResponse> future = testServiceFutureStub.testWz(TestProto.TestRequest.newBuilder().setName("wangze").build()); //异步 Futures.addCallback(future, new FutureCallback<TestProto.TestResponse>() { @Override public void onSuccess(TestProto.TestResponse result) { //成功的情况 System.out.println(result); } @Override public void onFailure(Throwable t) { //异常错误 System.out.println(t.getMessage()); } }, Executors.newCachedThreadPool()); System.out.println("后续操作......."); managedChannel.awaitTermination(20, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); } finally { managedChannel.shutdown(); } } }
如果想用SpringBoot整合grpc,可以看看yidongnan/grpc-spring-boot-starter: Spring Boot starter module for gRPC framework. (github.com)