本博客源码地址:https://github.com/wz20/gprc-learn
gRPC 是 Google 开源的一个高性能的 RPC(Remote Procedure Call) 框架。(由Google内部RPC项目Stubby演化而来,2015年正式开源)
主要使用场景:
核心设计思路:
要完成一个RPC通信的几个要素:
- 网络通信
- 协议
- 序列化
- 代理的创建
gRPC的好处:
HTTP1.x 协议
总结:HTTP1.x的传输方式为文本格式,可读性更好,但是效率更差。本质上是无法实现双工通信(HTTP1.1本身不能实现)。
HTTP2.0 协议
二进制协议,效率高,但可读性差
HTTP/2 使用头部压缩来减少传输数据的大小。这有助于减少网络带宽的使用量,并提高页面加载速度。
多路复用,只通过一个 TCP连接就可以传输所有的请求数据。多路复用可以绕过浏览器限制同一个域名下的请求数量的问题,进而提高了网页的性能。
HTTP/2 允许服务器在不被请求的情况下主动向客户端发送数据。这有助于减少请求延迟,并提高页面加载速度。
HTTP2.0 协议的核心概念:
Tip:更多HTTP2.0知识可以查看:[HTTP/2 协议(帧、消息、流简单的抓包分析)_http2 抓包-CSDN博客](https://blog.csdn.net/qq_38937634/article/details/111352895#:~:text=1.HTTP%2F2 核心概念 连接 Connection: 1个 TCP连接,包含一个或者多个 Stream。,数据流 Stream:一个双向通讯数据流,包含 1条或者多条 Message。 消息 Message%3A对应 HTTP%2F1中的请求或者响应,包含一条或者多条 Frame。)
Protocol Buffers是一种与语言无关、与平台无关的可扩展机制,用于序列化结构化数据。
它就像 JSON 一样,除了它是 更小、更快,并且它会生成本地语言绑定。您定义如何 您希望您的数据一次结构化,然后您可以使用特殊生成的 用于轻松编写和读取各种结构化数据的源代码 数据流并使用多种语言。
protobuf 需要安装protobuf的编译器,编译器的目的就是把protobuf的IDL语言,转换成具体某一种开发语言。
Protobuf 环境准备
下载编译器:
Release Protocol Buffers v3.19.6 · protocolbuffers/protobuf (github.com)
安装(win):解压缩并配置环境变量
验证:protoc --version
IDEA插件安装
文件格式
.proto
UserService.proto
xxxxx.proto
版本设定 (第一行)
syntax = "proto3";
注释
//单行注释
/* 多行注释 */
与Java语言相关的语法
//后续protobuf生成的java代码是一个源文件还是多个源文件
option java_multiple_files = false; //一个源文件
//指定protobuf生成的类放置在哪个包中
option java_package = "com.proto";
//protobuf生成java外部类(仅用于管理内部类,内部类才是我们真正使用的)的名称
option java_outer_classname = "ProtoOuter";
逻辑包(Java工程师用的较少)
//protobuf对于文件内容的管理
package xxx;
导入
import "xxxx/UserService.proto"
基本类型:Language Guide (proto 3) | Protocol Buffers Documentation (protobuf.dev)
枚举:
enum SEASON{
SPRING = 0;
SUMMER = 1;
}
//枚举的值 必须是0开始
消息:Message
message LoginRequest{
string username = 1; //username字段的编号
string password = 2;
int32 age = 3;
}
编号:从1开始,最大到2^29-1,注意:19000 - 19999 不可用,是protobuf自保留编号。
关键字:
protobuf 中可以定义多个message
message 是可以嵌套的
message 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【其中一个】
message SimpleMessage{
// test_oneOf 只能代表name 或 age 其中一个
oneof test_oneOf{
string name = 1;
int32 age = 2;
}
}
service HelloService{
//定义若干个服务的方法,定义服务接口
rpc hello(LoginRequest) returns(Result){};
}
show me code, 下面写一个gRPC的hello world程序!!!
项目结构:
编写 .proto 文件的IDL。
syntax = "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 转换成编程语言
protoc --java_out = /xxx/xxx/xx.proto
这种方式并不适用真实的开发环境,实战开发中,我们会采用maven的插件来进行编译并把他放置到具体位置。
配置maven插件
grpc/grpc-java: The Java gRPC implementation. HTTP/2 based RPC (github.com)
<dependencies>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-netty-shadedartifactId>
<version>1.58.0version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-protobufartifactId>
<version>1.58.0version>
dependency>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-stubartifactId>
<version>1.58.0version>
dependency>
<dependency>
<groupId>org.apache.tomcatgroupId>
<artifactId>annotations-apiartifactId>
<version>6.0.53version>
<scope>providedscope>
dependency>
dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.mavengroupId>
<artifactId>os-maven-pluginartifactId>
<version>1.7.1version>
extension>
extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>0.6.1version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}protocArtifact>
<pluginId>grpc-javapluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}pluginArtifact>
configuration>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>compile-customgoal>
goals>
execution>
executions>
plugin>
plugins>
build>
执行maven插件
compile执行成功后,可以在target中找到对应的代码:复制到开发目录下即可。
执行compile-custom命令,可以看到对应的代码:复制到开发目录下即可。
优化:我们可以通过自定义输出目录
<configuration> <protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}protocArtifact> <pluginId>grpc-javapluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}pluginArtifact> <outputDirectory>${basedir}/src/main/javaoutputDirectory> <clearOutputDirectory>falseclearOutputDirectory> configuration>
至此,api相关开发已完成。
代码简略分析:
- 实现业务接口 (添加具体功能)
- 创建服务器 (Netty)
引入api
<dependencies>
<dependency>
<groupId>org.examplegroupId>
<artifactId>rpc-grpc-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
实现业务接口
package 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();
}
}
创建服务端
package 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();
}
}
启动服务器
- 通过代理对象完成远端对象的调用
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的特点是:请求-响应式
当客户端发起调用后,会提交数据并等待服务端响应。
语法:
service HelloService{
rpc hello(HelloRequest) returns(HelloResponse){}
}
概念:一个请求对象,服务端可以回传多个结果对象。多个结果往往不是一起返回的
**使用场景:**服务端需要源源不断的给客户端返回数据。
交易所的k线图:
股票标号
client -----> Server
<-----
某一个时刻的股票行情
<-----
另外一个时刻的股票行情
语法:
service HelloService{
rpc hello(HelloRequest) returns(stream HelloResponse){}
}
show me code
在proto文件中添加一个service方法,并生成对应的Java代码:
service HelloService{
rpc hello(HelloRequest) returns(HelloResponse){}
rpc c2ss(HelloRequest) returns(stream HelloResponse){}
}
在service中实现业务逻辑
@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中实现的,所以不需要再次发布了)
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();
}
}
编写客户端
package 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: [email protected]
* @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开发
修改客户端的逻辑:
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处理结束......");
}
}
}
注意:客户端没有阻塞,所以拿到一个消息后执行完逻辑进程就关闭了。
**优化后的逻辑:**对客户端添加等待时间
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: [email protected]
* @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处理结束......");
}
}
}
运行结果:
服务端发来了一个消息: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"
概念:客户端发送多个请求对象,服务端只返回一个结果。
应用场景:IOT(物联网)
例如车上的传感器,源源不断的将车辆信号数据发送到服务端。服务端不需要一直响应给车。
编程语法:protobuf
service HelloService{
rpc cs2s(stream HelloRequest) returns(HelloResponse){}
}
show me Code
编写protobuf 并生成API
syntax = "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服务端
* @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();
}
};
}
开发客户端:
package 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: [email protected]
* @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();
}
}
}
运行结果
Client:
已连接到目标 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 cs2ss(stream HelloRequest) returns(stream HelloResponse){}
show me code
api
syntax = "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){}
}
开发服务端
@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();
}
};
}
开发客户端
package 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: [email protected]
* @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();
}
}
}
运行结果
client:
已连接到目标 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"
客户端消息全部接收成功
BlockingStub
阻塞 通信方式
Stub
异步 通过监听处理
FutureStub
同步 异步 NettyFuture
FutureStub只能应用于 简单RPC
FutureStub 案例:
1.首先创建一个新的proto文件,并生成API
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
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();
}
}
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.开发客户端
同步
package 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: [email protected]
* @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();
}
}
}
异步:
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());
//异步
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)