gRpc入门和springboot整合
gRpc入门和springboot整合
一、简介
1、gprc概念
gRpc是有google开源的一个高性能的pc框架,Stubby google内部的rpc,2015年正式开源,云原生时代一个RPC标准。
tips:异构系统,就是不同编程语言的系统。
2、grpc核心设计思路
grpc核心设计思路1. 网络通信 --> grpc自己封装网络通信的部分,提供多种语言的网络通信的封装(java(netty),c,go)的支持异构语言。2. 协议 --> http2协议,传输数据,支持二进制。支持双向流(双工)。连接的多路复用(NIO)3. 序列化 --> 基于文本Json,基于二进制(java原生序列化方式,Thrift二进制序列化,压缩二进制序列化)protobuf(protocol buffers)google开源的一种序列化方式。dubbo也可以用protobuf。 时间效率和空间效率是Json的3---5倍。定义了一套,IDL语言。4.代理的创建 --> 让调用者像调用本地方法那样,去调用远端的服务方法一样。stub.
3、gRpc和protobuf的区别
grpc是rpc框架。
protobuf是一种序列化方式
4、gRpc与ThrifRpc区别
共性:支持异构语言的 RPC
区别:1. 网络通信 Trift TCP 专属协议 grpc http22. 性能角度,thrift Rpc效率高于gRpc.3. gRpc 大厂背书(google),云原生时代 合作更好 集成更多,所以grpc应用更广泛
5、gRpc的好处
-
高效的进行进程间通信。
-
支持多种语言 。原生支持,C GO Java实现。c语言版本上扩展,c++,c#,node JS,python,ruby,php (二等公民)
-
支持多平台运行。 Linux Andriod IOS MacOS Windows
-
gRpc序列化方式采用protobuf,效率高
-
使用http2协议
-
大厂背书
二、http2.0协议
1、回顾 http1.x 协议
1.1、http1.0 协议
-
请求响应的模式。
-
短连接协议(无状态协议)。建立在tcp(长连接)上的,http主动断开连接(之前设备不好,主动断开)。
-
传输数据文本结构
-
单工,无法实现服务端推送。
- 变相实现服务推送,(采用客户端轮询)
1.2、http1.1 协议
- 请求相应的模式
- 有限的长连接(保持一段时间)
- 只能通过升级的方式,websocket协议,实现服务器想客户端推送。不属于http1.1的能力
1.3、总结http1.x协议特性:
- 传输数据文本格式,可读性好但是效率差。
- 本质上http1.x协议,无法实现双工通信。
- 资源的请求。需要发送多次请求,建立多个连接才可以完成。分流(静态资源分离,cdn缓存加速)
2、HTTP2.0协议
- Http2.0协议是一个二进制协议,效率高于Http1.x协议,可读性差。
- 可以实现双工通信
- 一个连接可以请求多个数据。【多路复用】
3、HTTP2.O协议的三个概念
- 数据流 Stream
- 消息 message
- 帧 frame
一个连接,有多个数据流。Stream.
3.1、requestMessage
一个数据流,有request Message.
Message,里面有frame.
第一个frame,对应的就是header。
- method:post
- version:http2.0
- user-agent:chrome
第二个frame,是body
- data
如果get,就是一个frame帧。没有data,body。
3.1、responseMessage
第一个frame,对应的就是head。
- status: 200
- version:http2.0
第二个frame,是body
- response数据
4、其他的相关概念
4.1、数据流的优先级
1. 数据流的优先级:可以为不同的Stream设置权重,来限制不同流的顺序。
1. 流控:client发送数据太快了,server处理不过来,通知client暂停数据的发送。
三、Protocol Buffers [protobuf]
1、简介
-
protobuf是一种与编程语言无关【IDL】,与具体的平台无关【OS】。他定义的中间语言,可以方便,在Client和server之间进行RPC的数据传输。
-
protobuf
- protobuf 2
- protobuf 3
目前主流应用的都是protobuf3.
-
protobuf主要安装protobuf的编译器,编译器目的,可以把protobuf的IDL语言,转换成具体某一种开发语言。
2、protobuf编译器安装
github上.
https://www.github.com/protocolbuffers/protobuf/releases
建议,64位,v3.19.6,是一个exe文件。
windows版本
1.直接解压缩,放置在一个特定的目录下面
2.直接配置环境变量 pathprotoc --versionmac版本brew install protobuf
3、protobuf IDEA插件
IDEA 新版本。原生支持,protocol Buffers.
-
2021.2版本后面的新版本,IDEA内置了Protobuf插件,和gRPC插件,都是默认勾选的。
-
2021.2版本之前的老版本,可以选装第三方protobuf插件。
-
二者不能共存
4、与Java语言相关的语法
#后续protobuf生成的java代码 一个源文件还是多个源文件 xx.java
option java_multiple_files = false; //生成一个源文件。#指定protobuf生成的类,放置在哪个包中
option java_package = "com.suns"; //java相关#指定的protobuf生成的外部类的名字(管理内部类【内部类才是真正开发使用】,相当于文件夹)
option java_outer_classname = "UserService";外部类.内部类.
5、逻辑包
#对于protobuf对于文件内容的管理
package xxx;
6、导入
UserService.protoOrderService.protoimort "xxx/UserService.proto"
7、protobuf 基本类型介绍
-
描绘RPC服务,远端接口。 Service(trift) gRpc也叫Service
-
结构体Struct(thrift) ; message(gRpc)
主要是看service和message如何定义。
protobuf的类型
https://www.protobuf.dev/programing-guides/proto3/
Scalar Value Types
A scalar message field can have one of the following types – the table shows the type specified in the .proto
file, and the corresponding type in the automatically generated class:
.proto Type | Notes | Java/Kotlin Type[1] |
---|---|---|
double | double | |
float | float | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | long |
uint32 | Uses variable-length encoding. | int[2] |
uint64 | Uses variable-length encoding. | long[2] |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | long |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | int[2] |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | long[2] |
sfixed32 | Always four bytes. | int |
sfixed64 | Always eight bytes. | long |
bool | boolean | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. | String |
bytes | May contain any arbitrary sequence of bytes no longer than 232. | ByteString |
8、枚举
enum SEASON{SPRING = 0;SUMMER = 1;}枚举的值 必须是0开始。
9、消息Message
9.1、基本定义
message LoginRequest{String username = 1;//代表编号。不是valuesingular String password = 2;int32 age = 3 ;
}
9.2、编号问题:
从1开始,到2^29-1,注意19000-19999 这几个编号不能用。这是protobuf自己保留的区间。
消息
- singular : 修饰messgae字段,这个字段只能是0个或1个 (默认关键字)0:null, 1:"123456"
- reapeated : 这个字段返回值是多个;等价于java Listmessage Result{string content = 1;reapeated string stutas = 2; //这个字段返回值是多个;等价于java List; Protobuf, 生成一个get方法: getStatusList()->List
}
9.3、protobuf [gRpc] 可以定义多个消息
message LoginRequest{
...
}message LoginResponse{
...
}
9.4、消息可以嵌套
message SearchResponse{message Result{string url = 1;string title =2;}string xxx = 1;string yyy = 2;Result ppp = 3;//内部。上面是定义message
}其他人调用:SearchResponse.Resultmessage AAA{string xxx = 1;SearchResponse.Result yyy = 2;
}
9.5、oneof [其中一个]
message SimpleMessage{//可取值,要么是string要么是int32oneof test_oneof{string name = 1;int32 age =2}test_oneof xxx;
}
9.6、服务
service HelloService{rpc hello(HelloRequest) returns(HelloResponse){}
}# 里面可以定义多个服务方法
# 定义多个服务接口
#gRpc 服务 4个服务方式.
4、第一个gRpc开发
4.1、项目结构
1. xxxx-api 模块定义protobuf idl语言并且通过命令创建具体的代码,后续client,server引入使用。1. message2. service
2. xxxx-server 模块1.实现api模块中定义的服务接口2.发布gRpc服务(创建服务器程序)
3. xxxx-client模块1. 创建服务端stub(代理)2. 基于代理(stub)RPC调用
4.2、api模块 grpc-api
1、proto文件 书写protobuf的IDL
2、protoc命令 把proto文件的IDL转换成编程语言。
#--java_out 表示 生成java --go_out,表示生成go
#--java_out=/xxx/xxx /xxx/xxx 表示位置
# /xxx/xxx/xx.proto 基于什么IDL的。 这个是源文件。
protoc --java_out=/xxx/xxx /xxx/xxx/xx.proto
3、实战】maven插件 进行protobuf IDL文件的编译,并把它放置IDEA具体位置。
4、github上有开源的插件。g
http://www.github.com/grpc/grpc-java
pom.xml<dependencies><dependency><groupId>io.grpc</groupId><artifactId>grpc-netty-shaded</artifactId><version>1.57.2</version><scope>runtime</scope></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>1.57.2</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>1.57.2</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>## protoc命令 生成message信息<protocArtifact>com.google.protobuf:protoc:3.22.3:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId>## protoc命令 生成service<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.57.2:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build>
${os.detected.classifier} maven内置变量;
获取当前 操作系统类型 windows -x86.
goal:maven goal;
compile;
compile-custom
5、源文件如下:
syntax = "proto3";option java_multiple_files = false;option java_package = "com.lizy";option java_outer_classname = "HelloProto";/*命名Hello.proto,outer_classname = HelloProtoIDL文件 目的 发布RPC服务,service---> message message<---------先定义message*/message HelloRequest{string name = 1;
}message HelloResponse{string result = 1;
}/*再定义service 需要让 protobuf转换 IDL语言。*/service HelloService{rpc hello(HelloRequest) returns (HelloResponse){}
}
这个字体蛮好看的
霞鹜文楷
使用 no meirye ui version 3.0.0
reg save "HKCU\Control Panel" .\reset_font.reg /yhttps://github.com/Tatsu-syo/noMeiryoUI
6、使用mavenHelper插件
可以使用ctrl+alt+r 快捷键导入。
7、使用maven的plugins中protobuf-maven-plugin插件
生成的文件会放在target里面。
-
需要执行两个命令
-
compile
-
compile-custom
-
然后再去手工移动生成的文件
过于麻烦。
8、从实战出发。
问题1:需要执行两个命令
解决:maven new goal,生成一个合并的命令
mvn xxx xxx;
Run maven goal;
new maven goal
点击需要的goal,点击两个,就可以直接有两个一起。
问题2:手工移动生成的文件太过麻烦
解决:在pom.xml文件中增加两行outputDirectory,clearOutputDirectory
<configuration>
<!-- ## protoc命令 生成message信息--><protocArtifact>com.google.protobuf:protoc:3.22.3:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId>
<!-- ## protoc命令 生成service--><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.57.2:exe:${os.detected.classifier}</pluginArtifact><outputDirectory>${basedir}/src/main/java</outputDirectory><!-- false 追加式生成--><clearOutputDirectory>false</clearOutputDirectory></configuration>
ctr+o 重写父类方法。
在 gRPC 中,BlockingStub, FutureStub, 和 Stub 是用于与 gRPC 服务进行交互的不同接口。它们的主要区别在于它们提供的方法调用的方式。BlockingStub:
BlockingStub 是 gRPC 提供的阻塞式接口。使用此接口,调用远程服务是阻塞的,也就是说,调用者必须等待服务器的响应才能继续执行。在调用方法后,你需要使用 .get() 方法来阻塞并等待结果。这是一个非常直接且易于理解的接口,但需要调用者等待,可能不适合处理大量并发的请求。例如:java
BlockingGrpc.newBlockingStub(channel).someRpc(request).get();
FutureStub:
FutureStub 也是阻塞式的接口,但与 BlockingStub 不同的是,它返回一个 Future 对象,这个对象表示一个尚未完成的计算或操作。你可以使用 Future.get() 来阻塞并等待结果,或者注册一个回调函数来在结果可用时自动获取。这种方式比 BlockingStub 更灵活,因为它允许你在等待结果的同时执行其他任务。例如:java
Futures.getUnchecked(future = asyncStub.someRpc(request));
Stub:
与前两者不同,Stub 是一个非阻塞的接口,它使用异步回调方式。当调用远程服务时,它不会立即返回结果,而是通过回调函数将结果传递给调用者。这种方式可以处理大量并发请求,并且可以更有效地利用系统资源。然而,与前两者相比,它可能需要更多的代码和更复杂的逻辑来处理异步回调。例如:java
channel.io().Schedulers.scheduleDirect(() -> {someRpc(request, new Callback() {@Overridepublic void onSuccess(Object result) {// handle the result}@Overridepublic void onFailure(Throwable t) {// handle the error}});
});
在选择使用哪种接口时,应根据你的应用需求和性能要求来决定。如果你需要简单直接的接口并且不介意阻塞等待结果,那么 BlockingStub 可能是最好的选择。如果你需要更灵活的处理方式并且愿意编写更多的代码来处理异步回调,那么 FutureStub 或 Stub 可能更适合你。
4.3、service模块 grpc-service
1、创建业务service
创建service模块,service类:HelloServiceImpl
业务端代码:
package com.lizy.service;import com.lizy.HelloProto;
import com.lizy.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {/*** 传统ssm* 1. 接受client的提交参数 request.getParameter()* 2. 业务处理 service+dao 调用对应的业务功能* 3. 提供返回值* @param request request* @param responseObserver responseObserver*/@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {//1. 接受client的请求参数String name = request.getName();//2. 业务处理 todo 基本处理这一块。System.out.println("name parameter: "+ 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();//处理后的响应的消息,通过网络回传 clientresponseObserver.onNext(helloResponse);//通知client响应已经结束 --> 标志responseObserver.onCompleted();}}
2、创建服务端
服务端代码:
public class GrpcServer1 {public static void main(String[] args) throws InterruptedException, IOException {//1. 绑定端口ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000);//2. 发布服务serverBuilder.addService(new HelloServiceImpl());//多个服务就add多个
// serverBuilder.addService(new UserServiceImpl());//3. 创建服务对象Server server = serverBuilder.build();server.start();server.awaitTermination();}
}
tips:pom.xml 父依赖 可以打印日志。
<dependencies><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.9</version></dependency></dependencies>版本太高会有问题。
4.4、client模块 grpc-client
1、client通过代理对象完成远端对象的调用。
package com.lizy;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;public class GrpcClient1 {public static void main(String[] args) {//1. 创建通信的管道ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();//2. 获得代理对象 stub 存根。try {HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);//3.完成RPC调用//3.1 准备参数HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("李君临");HelloProto.HelloRequest helloRequest = builder.build();//3.2 进行功能rpc调用,获取响应的内容HelloProto.HelloResponse helloResponse = helloService.hello(helloRequest);String result = helloResponse.getResult();System.out.println("result = "+result);} catch (Exception e) {throw new RuntimeException(e);} finally {managedChannel.shutdown();}}
}
注意事项
client会去监听【grpc做的】 responseObserver.onCompleted();
//处理后的响应的消息,通过网络回传 client
responseObserver.onNext(helloResponse);
//通知client响应已经结束 --> 标志 ;通知client 整个服务结束,底层返回标记。
responseObserver.onCompleted();requestObserver.onNext(helloRequest1);
requestObserver.onCompleted();
5、gRpc的四种通信方式
5.1、四种通信方式
- 简单rpc 一元rpc(unary RPC)
- 服务端流式RPC( Server Streaming RPC)
- 客户端流式RPC(Client Streaming RPC)
- 双向流RPC(Bi-directional Stream RPC)
5.2、简单RPC(一元RPC)
上面第一个RPC程序,实际上就是一元RPC
1、特点
- 当client发起调用后,提交数据,并且等待服务端响应。
- 开发过程中,主要采用就是一元RPC的这种通信方式。
2、语法
service HelloService{rpc hello(HelloRequest) returns (HelloResponse){}
}
5.3、服务端流式RPC
一个请求对象,服务端可以回传多个结果对象。
1、特点
一个请求对象,服务端可以回传多个结果对象。
2、使用场景
client --------> server股票标号<----------某一个时刻的 股票的行情
3、语法
返回值,增加stream 关键字
service HelloService{rpc hello(HelloRequest) returns (stream HelloResponse){} //服务端流式RPCrpc hello(HelloRequest1) returns (HelloResponse1){} //一元RPC
}
4、开发实践
4.1、代码:
1、proto增加c2ss方法;
service HelloService{rpc hello(HelloRequest) returns (HelloResponse){}rpc c2ss(HelloRequest) returns (stream HelloResponse){}
}
2、serviceImpl重写c2ss方法
@Overridepublic void c2ss(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {//1、接收client的请求参数String name = request.getName();//2、完成业务的处理System.out.println("name = "+ name);//3、根据业务处理的结果,提供响应。for (int i = 0; i < 9; i++) {HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();builder.setResult("处理的结果: "+ i);HelloProto.HelloResponse helloResponse = builder.build();responseObserver.onNext(helloResponse);//增加一个睡眠,模仿不同时刻try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}responseObserver.onCompleted();}
3、client发起响应
package com.lizy;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;import java.util.Iterator;public class GrpcClient2 {public static void main(String[] args) {//1. 创建通信的管道ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();//2. 获得代理对象 stub 存根。try {HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);//3.完成RPC调用//3.1 准备参数HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("李君临");HelloProto.HelloRequest helloRequest = builder.build();//3.2 进行功能rpc调用,获取响应的内容Iterator<HelloProto.HelloResponse> helloResponseIterator = helloService.c2ss(helloRequest);while (helloResponseIterator.hasNext()){HelloProto.HelloResponse helloResponse = helloResponseIterator.next();String result = helloResponse.getResult();System.out.println("result = "+result);}} catch (Exception e) {e.printStackTrace();} finally {managedChannel.shutdown();}}
}
结果示例:
5、使用监听异步方式。
5.1、使用stub,而不是blockingstub
5.2、api,服务端代码不变
5.3、客户端代码如下
package com.lizy;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;import java.util.concurrent.TimeUnit;public class GrpcClient3 {public static void main(String[] args) {//1. 创建通信的管道ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();//2. 获得代理对象 stub 存根。try {HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel);//3.完成RPC调用//3.1 准备参数HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("李君临");HelloProto.HelloRequest helloRequest = builder.build();//3.2 进行功能rpc调用,获取响应的内容//StreamObserver 观察者模式helloServiceStub.c2ss(helloRequest, new StreamObserver<HelloProto.HelloResponse>() {@Overridepublic void onNext(HelloProto.HelloResponse helloResponse) {//服务端 响应了 一个消息后,需要立即处理话,把代码写在这里。System.out.println("服务端每一次响应的消息 "+ helloResponse.getResult());}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onCompleted() {//需要把服务端响应的所有数据拿到后,再进行业务处理。//服务端 响应了 一个消息后,需要立即处理话,把代码写在这里。System.out.println("服务端响应结束,后续可以根据需要,在这里统一处理服务端响应的内容");}});managedChannel.awaitTermination(12, TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();} finally {managedChannel.shutdown();}}
}
5.4、注意问题
异步编程,因为没有阻塞,顺序执行了代码,直接shutdown了。
服务端想响应,都没办法响应。因为client已经关掉了通道。
5.4、客户端流式RPC
1、特点
客户端发送多个请求对象,服务只返回一个结果。
2、使用场景
IOT(互联网【传感器】)
这些终端给服务端发送数据。
传感器会经常性地给服务端发消息。
3、语法
proto
rpc cs2s(stream HelloRequest) returns (HelloResponse){}
4、开发
4.1、api
rpc cs2s(stream HelloRequest) returns (HelloResponse){}
4.2、服务端开发
@Overridepublic StreamObserver<HelloProto.HelloRequest> cs2s(StreamObserver<HelloProto.HelloResponse> responseObserver) {return new StreamObserver<HelloProto.HelloRequest>() {@Overridepublic void onNext(HelloProto.HelloRequest helloRequest) {System.out.println("服务端收到一条消息:"+helloRequest.getName());}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onCompleted() {System.out.println("全部接收完成");HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();builder.setResult("this is result");HelloProto.HelloResponse helloResponse = builder.build();responseObserver.onNext(helloResponse);responseObserver.onCompleted();}};}
4.3、客户端开发
package com.lizy;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;import java.util.concurrent.TimeUnit;public class GrpcClient1 {public static void main(String[] args) {ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();try {HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel);StreamObserver<HelloProto.HelloRequest> requestStreamObserver = helloServiceStub.cs2s(new StreamObserver<HelloProto.HelloResponse>() {@Overridepublic void onNext(HelloProto.HelloResponse helloResponse) {System.out.println("服务端 响应的数据内容为:" + helloResponse.getResult());}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onCompleted() {System.out.println("服务端响应结束");}});for (int i = 0; i < 10; i++) {//对应的请求。每个请求。//先定义返回,在定义请求。HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("李"+i);HelloProto.HelloRequest request = builder.build();requestStreamObserver.onNext(request);Thread.sleep(1000);}requestStreamObserver.onCompleted();managedChannel.awaitTermination(12, TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();}finally {managedChannel.shutdown();}}
}
如果在服务端处理next返回。
会提示
警告: Cancelling the stream with status Status{code=INTERNAL, description=Too many responses, cause=null}
5.6、双向流式RPC
1、特点
客户端可以发送多个请求消息,服务端响应多个响应消息
2、应用场景
聊天室
3、语法
rpc cs2ss(stream HelloRequest) returns (stream HelloResponse){}
4、开发
4.1、api
rpc cs2ss(stream HelloRequest) returns (stream HelloResponse){}
4.2、server
@Overridepublic StreamObserver<HelloProto.HelloRequest> cs2ss(StreamObserver<HelloProto.HelloResponse> responseObserver) {return new StreamObserver<HelloProto.HelloRequest>() {@Overridepublic void onNext(HelloProto.HelloRequest helloRequest) {System.out.println("服务端收到一条消息:" + helloRequest.getName());responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("response "+helloRequest.getName() + "result ").build());}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onCompleted() {System.out.println("全部接收完成");responseObserver.onCompleted();}};}
4.3、client
public class GrpcClient5 {public static void main(String[] args) {ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();try {HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(managedChannel);StreamObserver<HelloProto.HelloRequest> requestStreamObserver = helloServiceStub.cs2ss(new StreamObserver<HelloProto.HelloResponse>() {@Overridepublic void onNext(HelloProto.HelloResponse helloResponse) {System.out.println("服务端 响应的数据内容为:" + helloResponse.getResult());}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onCompleted() {System.out.println("服务端响应结束");}});for (int i = 0; i < 10; i++) {//对应的请求。每个请求。//先定义返回,在定义请求。HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("李"+i);HelloProto.HelloRequest request = builder.build();requestStreamObserver.onNext(request);Thread.sleep(1000);}requestStreamObserver.onCompleted();managedChannel.awaitTermination(12, TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();}finally {managedChannel.shutdown();}}
}
6、gRpc的代理方式
1. BlocingStub 阻塞 通信方式
2. Stub异步 通过监听处理的
3. FutureStub同步 / 异步 NettyFuture1. FutureStub 只能应用一元RPC
6.1、futureStub代码
package com.lizy;import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class GrpcClient6 {public static void main(String[] args) {ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();try {HelloServiceGrpc.HelloServiceFutureStub helloServiceFutureStub = HelloServiceGrpc.newFutureStub(managedChannel);ListenableFuture<HelloProto.HelloResponse> listenableFuture = helloServiceFutureStub.hello(HelloProto.HelloRequest.newBuilder().setName("李1").build());//阻塞操作。
// HelloProto.HelloResponse helloResponse = listenableFuture.get();
// System.out.println(helloResponse.getResult());//第一种异步操作
// listenableFuture.addListener(()->{
// System.out.println("异步的rpc响应回来了");
// }, Executors.newCachedThreadPool());//第二种异步操作。Futures.addCallback(listenableFuture, new FutureCallback<HelloProto.HelloResponse>() {@Overridepublic void onSuccess(HelloProto.HelloResponse result) {System.out.println("异步的rpc响应回来了"+result.getResult());}@Overridepublic void onFailure(Throwable t) {}}, Executors.newCachedThreadPool());System.out.println("后续的操作...");managedChannel.awaitTermination(12, TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();}finally {managedChannel.shutdown();}}
}
7、gRpc与SpringBoot整合
7.1、gRpc与SpringBoot整合的思想
1. grp-server
2. grpc-client
7.2、解析
Springboot与gRpc整合过程中,对于服务端做了什么封装。
7.3、搭建开发环境
缺少依赖
cannot access com.google.protobuf.GeneratedMessageV3
解决方法:在pom中添加相关依赖
<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.5.1</version>
</dependency>
7.4、服务端开发
1、pom依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.lizy</groupId><artifactId>grpc-api</artifactId><version>1.0-SNAPSHOT</version><exclusions><exclusion><artifactId>error_prone_annotations</artifactId><groupId>com.google.errorprone</groupId></exclusion></exclusions></dependency><dependency><groupId>net.devh</groupId><artifactId>grpc-server-spring-boot-starter</artifactId><version>2.14.0.RELEASE</version><exclusions><exclusion><artifactId>error_prone_annotations</artifactId><groupId>com.google.errorprone</groupId></exclusion><exclusion><artifactId>protobuf-java</artifactId><groupId>com.google.protobuf</groupId></exclusion></exclusions></dependency></dependencies>
2、yaml配置
# 核心配置的 就是gRpc的端口号
spring:application:name: boot-server
# 取消 tomcat启动。netty服务可能也要这样做main:web-application-type: none
grpc:server:port: 9000
3、service配置
package com.liziyao.grpcbootsever.service;import com.lizy.HelloProto;
import com.lizy.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;@GrpcService
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {String name = request.getName();System.out.println("request 请求的name:" + name);responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("恭喜你返回成功").build());responseObserver.onCompleted();}
}
7.5、客户端开发。
分析:
1、pom依赖。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.lizy</groupId><artifactId>grpc-api</artifactId><version>1.0-SNAPSHOT</version><exclusions><exclusion><artifactId>error_prone_annotations</artifactId><groupId>com.google.errorprone</groupId></exclusion></exclusions></dependency><dependency><groupId>net.devh</groupId><artifactId>grpc-client-spring-boot-starter</artifactId><version>2.14.0.RELEASE</version><exclusions><exclusion><artifactId>error_prone_annotations</artifactId><groupId>com.google.errorprone</groupId></exclusion></exclusions></dependency></dependencies>
2、yaml配置
spring:application:name: boot-clientgrpc:client:grpc-server:address: 'static://127.0.0.1:9000'negotiation-type: plaintext
3、client配置
package com.liziyao.grpcbootclient.controller;import com.lizy.HelloProto;
import com.lizy.HelloServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@GrpcClient("grpc-server")private HelloServiceGrpc.HelloServiceBlockingStub stub;@GetMapping("hello")public String hello(String name){if (StringUtils.isEmpty(name)) {name = "扶摇直上";}System.out.println("你好,进入了client方法");HelloProto.HelloResponse response = stub.hello(HelloProto.HelloRequest.newBuilder().setName(name).build());String result = response.getResult();return result;}
}
4、返回结果
8、高级用法
1. 拦截器 一元拦截器
2. Stream Tracer [监听器] 流拦截器
3. Retry Policy 客户端重试
4. NameResolver consule | ectd | nacos
5. 负载均衡 (pick-first,轮询)
6. grpc与微服务的整合序列化(protobuf) Dubbogrpc Dubbo dubbo只做服务治理grpc gatewaygrpc JWTgrpc nacos2.0grpc 替换 OpenFeign7. grpc http2.x 多种通信方式。能和K8S 作整合。
相关文章:
gRpc入门和springboot整合
gRpc入门和springboot整合 一、简介 1、gprc概念 gRpc是有google开源的一个高性能的pc框架,Stubby google内部的rpc,2015年正式开源,云原生时代一个RPC标准。 tips:异构系统,就是不同编程语言的系统。 2、grpc核心设计思路 grpc核心设计…...
基于FPGA点阵显示屏设计-毕设
本设计是一1616点阵LED电子显示屏的设计。整机以EP2C5T144C8N为主控芯片,介绍了以它为控制系统的LED点阵电子显示屏的动态设计和开发过程。通过该芯片控制一个行驱动器74HC154和两个列驱动器74HC595来驱动显示屏显示。该电子显示屏可以显示各种文字或单色图像,采用4块8 x 8点…...
Rocky9.2基于http方式搭建局域网yum源
当前负责的项目有几十台Linux服务器,在安装各类软件的时候需要大量依赖包,而项目部署的环境属于内网环境,与Internet网完全隔离,无法采用配置网络yum源的方式安装rpm包,直接在每台linux服务器上配置本地yum源也比较麻烦,而采用直接下载rpm包用rpm命令安装更是费时费力。所…...
Android 串口通讯
Serial Port Android 串口通讯 arm64-v8a、armeabi-v7a、x86、x86_64 AAR 名称操作serial.jar下载arm64-v8a下载armeabi-v7a下载x86下载x86_64下载arm-zip下载x86-zip下载 Maven 1.build.grade | setting.grade repositories {...maven { url https://jitpack.io } }2./a…...
论如何在Android中还原设计稿中的阴影
每当设计稿上注明需要添加阴影时,Android上总是显得比较棘手,因为Android的阴影实现方式与Web和iOS有所区别。 一般来说阴影通常格式是有: X: 在X轴的偏移度 Y: 在Y轴偏移度 Blur: 阴影的模糊半径 Color: 阴影的颜色 何为阴影 但是在A…...
Hadoop生态圈中的Flume数据日志采集工具
Hadoop生态圈中的Flume数据日志采集工具 一、数据采集的问题二、数据采集一般使用的技术三、扩展:通过爬虫技术采集第三方网站数据四、Flume日志采集工具概述五、Flume采集数据的时候,核心是编写Flume的采集脚本xxx.conf六、Flume案例实操1、采集一个网络…...
FFmpeg获取媒体文件的视频信息
视频包标志位 代码 printf("index:%d\n", in_stream->index);结果 index:0视频帧率 // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧 printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));结果 fps:29.970070fps…...
io概述及其分类
一、IO概念 • I/O 即输入Input/ 输出Output的缩写,其实就是计算机调度把各个存储中(包括内存和外部存储)的数据写入写出的过程; I : Input O : Output 通过IO可以完成硬盘文件的读和写。 • java中用“流(stream&am…...
前端面试话术集锦第 14 篇:高频考点(React常考基础知识点)
这是记录前端面试的话术集锦第十四篇博文——高频考点(React常考基础知识点),我会不断更新该博文。❗❗❗ 1. 生命周期 在V16版本中引入了Fiber机制。这个机制一定程度上的影响了部分生命周期的调用,并且也引入了新的2个API来解决问题。 在之前的版本中,如果你拥有一个很…...
UI/UX+前端架构:设计和开发高质量的用户界面和用户体验
引言 随着数字化和互联网的普及,越来越多的企业和组织需要高质量的用户界面和用户体验,以及可靠、高效的前端架构。UI/UX设计师和前端架构师可以为这些企业和组织提供所需的技术和创意支持。本文将介绍UI/UX前端架构这个方向,包括设计原则、…...
长尾关键词挖掘软件-免费的百度搜索关键词挖掘
嗨,大家好!今天,我想和大家聊一聊长尾关键词挖掘工具。作为一个在网络世界里摸爬滚打多年的人,我对这个话题有着一些个人的感悟和见解,希望能与大家分享。 首先,让我坦白一点,长尾关键词挖掘工具…...
React Native 环境配置(mac)
React Native 环境配置(mac) 1.Homebrew2.Node.js、WatchMan3.Yarn4.Android环境配置1.安装JDK2.下载AndroidStudio1.国内配置 Http Proxy2.安装SDK1.首先配置sdk的路径2.SDK 下载 3.创建模拟器4.配置 ANDROID_HOME 环境变量 5.IOS环境1.升级ruby&#x…...
CAD for JS:VectorDraw web library 10.1004.1 Crack
VectorDraw web library经过几年的研究,通过互联网展示或工作的可能性并拒绝了各种项目,我们最终得出的结论是,在 javascript 的帮助下,我们将能够在 Microsoft IE 以外的互联网浏览器中通过网络演示矢量图形(支持 ocx…...
代码管理工具git1
ctrl 加滚轮 放大字体 在计算机任意位置单击右键,选择::Git Bash Here git version git清屏命令:ctrl L查看用户名和邮箱地址: $ git config user.name$ git config user.email修改用户名和邮箱地址:$ git…...
层次聚类分析
1、python语言 from scipy.cluster import hierarchy # 导入层次聚类算法 import matplotlib.pylab as plt import numpy as np# 生成示例数据 np.random.seed(0) data np.random.random((20,1))# 使用树状图找到最佳聚类数 Z hierarchy.linkage(data,methodweighted,metric…...
Jmeter性能实战之分布式压测
分布式执行原理 1、JMeter分布式测试时,选择其中一台作为调度机(master),其它机器作为执行机(slave)。 2、执行时,master会把脚本发送到每台slave上,slave 拿到脚本后就开始执行,slave执行时不需要启动GUI࿰…...
学信息系统项目管理师第4版系列08_管理科学基础
1. 科学管理的实质 1.1. 反对凭经验、直觉、主观判断进行管理 1.2. 主张用最好的方法、最少的时间和支出,达到最高的工作效率和最大的效果 2. 资金的时间价值与等值计算 2.1. 资金的时间价值是指不同时间发生的等额资金在价值上的差别 2.2. 把资金存入银行&…...
从2023蓝帽杯0解题heapSpary入门堆喷
关于堆喷 堆喷射(Heap Spraying)是一种计算机安全攻击技术,它旨在在进程的堆中创建多个包含恶意负载的内存块。这种技术允许攻击者避免需要知道负载确切的内存地址,因为通过广泛地“喷射”堆,攻击者可以提高恶意负载被…...
基于SSM的学生宿舍管理系统设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...
jvm 内存模型介绍
一、类加载子系统 1、类加载的过程:装载、链接、初始化,其中,链接又分为验证、准备和解析 装载:加载class文件 验证:确保字节流中包含信息符合当前虚拟机要求 准备:分配内存,设置初始值 解析&a…...
用Jmeter进行压测详解
简介: 1.概述 一款工具,功能往往是很多的,细枝末节的地方也很多,实际的测试工作中,绝大多数场景会用到的也就是一些核心功能,根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲…...
Mysql001:(库和表)操作SQL语句
目录: 》SQL通用规则说明 SQL分类: 》DDL(数据定义:用于操作数据库、表、字段) 》DML(数据编辑:用于对表中的数据进行增删改) 》DQL(数据查询:用于对表中的数…...
甲骨文全区登录地址
日本东部 东京 https://console.ap-tokyo-1.oraclecloud.com https://console.ap-tokyo-1.oraclecloud.com 日本中部 大阪 https://console.ap-osaka-1.oraclecloud.com https://console.ap-osaka-1.oraclecloud.com 韩国中部 首尔 https://console.ap-seoul-1.oraclecloud.c…...
Java面试题第八天
一、Java面试题第八天 1.如何实现对象克隆? 浅克隆 浅克隆就是我们可以通过实现Cloneable接口,重写clone,这种方式就叫浅克隆,浅克隆 引用类型的属性,是指向同一个内存地址,但是如果引用类型的属性也进行浅克隆就是深…...
什么是同步容器和并发容器的实现?
同步容器和并发容器都是用于在多线程环境中管理数据的容器,但它们在实现和用法上有很大的区别。 同步容器: 同步容器是使用传统的同步机制(如synchronized关键字或锁)来保护容器内部数据结构的线程安全容器。同步容器通常是单线…...
学Python的漫画漫步进阶 -- 第十六步
学Python的漫画漫步进阶 -- 第十六步 十六、多线程16.1 线程相关的知识16.1.1 进程16.1.2 线程16.1.3 主线程 16.2 线程模块——threading16.3 创建子线程16.3.1 自定义函数实现线程体16.3.2 自定义线程类实现线程体 16.4 线程管理16.4.1 等待线程结束16.4.2 线程停止 16.5 动动…...
MySQL 8.0 OCP (1Z0-908) 考点精析-架构考点5:数据字典(Data Dictionary)
文章目录 MySQL 8.0 OCP (1Z0-908) 考点精析-架构考点5:数据字典(Data Dictionary)File-based Metadata Storage (基于文件的元数据存储)Transactional Data Dictionary (事务数据字典)Serialized Dictionary Informat…...
7分钟了解ChatGPT是如何运作的
ChatGPT是现在最为热门的聊天助手应用,它使用了一个大型语言模型(LLM),即GPT-3.5。它通过大量的文本数据进行训练,以理解和生成人类语言。但是,你是否有了解过ChatGPT是如何运作的吗? 下面我们就一起通过这个视频来一起…...
蓝桥杯打卡Day8
文章目录 C翻转矩阵幂 一、C翻转IO链接 本题思路:本题需要找出顺时针旋转和逆时针旋转的规律,然后就可以解决该问题。 矩阵顺时针90旋转规律:列号变为行号,(n-行号-1)变为列号 规律:a[i][j]b[j][n-i1]; 矩阵逆时针90旋转规律:行号变为列号࿰…...
React 学习笔记目录
学习使用的开发工具 编译器 VSCode 开发语言工具 TypeScript /JavaScript 重要程度分类 一般 这个程度的知识点主要是达到熟练掌握即可,不用太深入研究和学习。 重要 这个程度的知识点主要是达到熟练掌握,并且内部的原理切要熟记,因为会关…...
mysql 大型网站开发/成品影视app开发
通常,说到Hook键盘鼠标,总需要一大堆代码,涉及各种不明白的API.而在DSAPI中,可以说已经把勾子简化到不能再简化的地步.甚至不需要任何示例代码即会使用.那么如何实现呢? Private WithEvents HK As New DSAPI.键盘鼠标钩子注意上面带了WithEvent. 写完上面那句后,即可选择该HK,…...
网站的交互体验/搜索引擎优化的目的是对用户友好
上海工程技术大学C语言考试试卷一、选择题(本题共15小题,每小题2分,共30分)1.下列字符串能作为变量名的是()A)3int B)float C)_2xy D)break2.以下选项中可作为C语言合法整数的是(A)1010B B)0287 C)0x02h3 D)0x0ffa3.下列正确的C语句是(A)x2 B)scanf(“%d…...
郑州一网网站建设/自己怎么搭建网站
开始学saltstack的时候是在现在一家做CDN加速的,同步下发的用到这个工具,下面我简单介绍和操作给大家看下。Salt 和 Puppet Chef 一样可以让你同时在多台服务器上执行命令也包括安装和配置软件。Salt 有两个主要的功能:配置管理和远程执行。S…...
做网站教学书/推广平台怎么找客源
Tomcat对于J2EE或Java web开发者而言绝不陌生,但说到Realm,可能有些人不太清楚甚至没有听说过,那么到底什么是Realm?简单一句话就是:Realm是Tomcat中为web应用程序提供访问认证和角色管理的机制。配置了Realmÿ…...
酒类网站如何做/推广网站源码
定期更新新媒体知识,在学习之中,有问题可以留言,大家交流,谢谢!新媒体平台视觉呈现 头像:表明账号主体,加深印象 名称:账号的名字,最好能说明账号内容领域或主题&#x…...
三种网络营销方式/聊城seo优化
一. __new__ 和 __init__ __new__ : 创建对象 __init__ : 初始化对象 class Foo(object):def __init__(self):print("初始化对象")def __new__(cls, *args, **kwargs):print("创建对象")return object.__new__(cls)obj Foo()# 以上代码执行结果是: # 创建…...