4. gRPC4.1 gRPC介绍1. gRPC是由google开源的一个高性能的RPC框架。Stubby Google内部的RPC,演化而来的，2015正式开源。云原生时代是一个RPC标准。 2. gRPC核心的设计思路 1．网络通信 --->gRPC自己封装网络通信的部分提供多种语言的网络通信的封装(C Java[Netty] Go) 2．协议---> HTTP2传输数据的时候二进制数据内容。支持双向流(双工）连接的多路复用。 3．序列化--->基本文本JSON基于二进制Java原生序列化方式 Thrift二进制的序列化压缩二级制序列化。 protobuf (Protocol Buffers) google开源一种序列化方式︰时间效率和空间效率是JSON的3---5倍。 IDL语言 4．代理的创建--->让调用者像调用本地方法那样去调用远端的服务方法。 stub 3. gRPC 与ThriftRPC区别 共性:支持异构语言的RPC。 区别: 1．网络通信 Thrift TCP 专属协议 GRPC HTTP2 2．性能角度ThriftRPC 性能高于gRPC 3. gRPC 大厂背书(Google)，云原生时代与其他组件合作的顺利。所以gRPC应用更广泛。 4. gRPC的好处 1．高效的进行进程间通信。 2．支持多种语言原生支持C Go Java实现。C语言版本上扩展C++C# NodeJs Python Ruby PHP.. 3．支持多平台运行 Linux Android IOS Mac0s windows。 4. gPRC序列化方式采用protobuf，效率高。 5．使用Http2协议 6．大厂的背书 4.2 Http2.0协议1. 回顾 Http1.x协议 Http1.0协议 请求响应的模式 短链接协议（无状态协议） 传输数据文本结构 单工 无法实现服务的推送 变相实现推送（客户端轮询的方式） Http1.1协议 请求响应的模式 有限的长连接 升级的方式WebSocket 双工 实现服务器向客户端的推送。 管道网络传输(wei'bei's) 总结Http1.x协议 共性 1. 传输数据文本格式 可读性好 但是效率差。 2. 本质上Http1.x协议无法实现双工通信。 3. 资源的请求。需要发送多次请求，建立多个连接才可以完成。 2. HTTP2.0协议 1. http2.0协议是一个二进制协议，效率高于Http1.x协议，可读性差。 2. 可以实现双工通信 3. 一个请求，一个链接 可以请求多个数据。【多路复用】（如下图） 3. Http2.0协议的三个概念 1. 数据liu stream 2. 消息 message 3. 帧 frame 4. 其他的相关概念 1. 数据流的优先级，可以通过为不同的stream设置权重，来限制不同流的传输顺序。 2. 流控 --client发送的数据太快了，server处理不过来，通知client暂停数据的发送。4.3 Protocol buffers[protobuf]1. protobuf 是一种与变成语言无关【IDL】，与集体的平台无关【OS】。 它定义的中间语言，可以方便的在clinet 与server中进行RPC的数据传输。 2. protobuf 两个版本 proto2 proto3，但是目前主流应用都是proto3. 3. protobuf 主要安装protobuf的编译器，编译器的目的，可以把protobf的IDL语言，转换成具体的某一种语言。4.3.1 protobuf的编译器安装IDE 集成4.3.2 protobuf的语法文件格式xxx.proto UserServce.proto ORderService.proto版本设定syntax = "proto3"； (IDL语言)注释1. 单行注释 // 2. 多行注释 /* */与Java语言相关的语法optin 关键字开头的 可以选择使用语法 # 后续protobuf生成的java代码 一个源文件还是多个源文件 xx.java optin java_multiple_files = false; (true为多个或者false一个) 一般情况为false # 指定protobuf生成的类 放置在哪个包中 optin jva_package= "com.suns"; # 指定protobuf生成的外部类的名字（管理内部类【内部类才是正真开发使用】） optin java_outer_classname = "UserName"; (外部类)逻辑包（了解）# 对于protobuf对于文件内容管理 package xxx；导入UserService.proto OrderService.proto import "xxx/UserService.proto";基本类型枚举enm SEASON{ SPRING = 0; SUMMER=1; } 枚举的值 必须是0开始消息 messagesmessages LoginRequset { string username = 1; string password = 2； int32 age = 3； } 编号 从1开始 到2^29-1 注意：19000 -19999 不能用这个区间内的编号，因为他是protobuf自己保留的。 (关键字) - singular ：这个字段的值 只能是0个或1个（默认关键字） null "123456" - repwated message Result{ string conten = 1; repeated string stutas =2; //这个字段 返回值 等价于 java List Protobuf getStatusList() -->List } protobuf[grpc] 可以定义多个消息 message LonginRequest{ .... } message LoginRespone { .... } 消息可以被嵌套 message SerchResponse{ message Result{ string url =1; string titlr = 2; } string xxx =1; int32 yyy =2; Result ppp = 3; } SerchResponse.Result message AAA { string xxx =1; SearchResponse.Result yyy =2; } oneof[其中一个] message SimpleMssage{ onof test_oneof{ string name = 1; int32 age =2; } test_oneof xxx }服务service HelloService{ rpc hello(HelloRequest) returns(HelloResponse){} } # 里面可以定义多个服务方法 # 定义多个服务接口 # gRPC 服务 4个服务方式4.3.3 第一个gRPC的开发项目结构1. xxx-api 模块 定义 protobuf IDL语言 并且通过命令创建具体的代码，后续client server引入使用 1. message 2. service 2. xxx-server 模块 1. 实现api模块中定义的服务接口 2. 发布gPRC服务（创建服务端程序） 3. xxx-clien 模块 1. 创建服务端stub（代理） 2. 基于代理 (stub) PRC调用。api1. .proto文件 书写 protobuf的IDL 2. protoc命令 把proto文件的IDL 转换成编程语言 protoc ---java_out=/xxx/xxx /xxx/xxx/xx.proto 3. 【实战】 mmaven插件 进行protobuf IDL文件的编译，并把它放置IDEA具体位置 pom.xml io.grpc grpc-netty-shaded 1.58.0 runtime io.grpc grpc-protobuf 1.58.0 io.grpc grpc-stub 1.58.0 org.apache.tomcat annotations-api 6.0.53 provided kr.motd.maven os-maven-plugin 1.7.1 org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier} ${basedir}/src/main/java false compile compile-custom xxx-server 服务端模块的开发1. 实现业务接口 添加具体的功能 （Mybatis+Mysql） package cn.zzrg.service; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.stub.StreamObserver; /** * @Author ZzRG * @Date 2023/9/24 16:48 * @Version 1.0 */ public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { /** * 1. 接受client提交的参数 * 2. 业务处理 service + dao 调用对应的业务功能。 * 3. 提供返回值 * @param request * @param responseObserver */ @Override public void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) { // 1. 接收客户端的请求参数 String name = request.getName(); // 2. 业务处理 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(); responseObserver.onNext(helloResponse); responseObserver.onCompleted(); } } 2. 创建服务端 (Netty) package cn.zzrg.service; import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; /** * @Author ZzRG * @Date 2023/9/24 16:57 * @Version 1.0 */ public class GrpcServer1 { public static void main(String[] args) throws IOException, InterruptedException { // 1.绑定端口 ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000); // 2.发布服务 serverBuilder.addService(new HelloServiceImpl()); // 3.创建服务对象 Server server = serverBuilder.build(); server.start(); server.awaitTermination(); } } xxx-client 模块1. client通过代理对象完成远程调用 package cn.zzrg; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient { 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("ZzRG"); 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(); } } }注意事项服务端 处理返回值时 responseObserver.onNext(helloResponse1); // 通过这个方法 把响应的消息 回传client responseObserver.onCompleted(); //通过client 整个服务结束。 底层返回标记 // clinet就会监听标记[grpc做的] responseObserver.onNext(helloRequest1); responseObserver.onCompleted();4.3.4 gRPC的四种通讯方式四种通讯方式1. 简单rpc 一元rpc（Unary RPC） 2. 服务端流式RPC (Server Streaming RPC) 3. 客户端流式RPC (Client Streaming RPC) 4. 双向流RPC (Bi-directional Stream RPC)简单RPC（一元RPC）1. 第一个RPC程序，实际上就是一元RPC特点当client发起调用后，提交数据，并且等待 服务端响应。 开发过程中，主要采用的就是一元RPC的这种通讯方式语法service HelloService{ rpc hello(HelloRequest) returns (HelloResponse){} rpc hello1(HelloRequest1) returns(HelloResponse1){} }服务端流式RPC一个请求对象，服务端可以回传多个结果对象特点使用场景client ----------> Server 股票的编号 <--------- 某一个时刻的 股票行情proto语法service HelloService{ rpc hello(HelloRequest) returns (stream HelloResponse){} //【服务端流式RPC】 rpc hello1(HelloRequest1) returns(HelloResponse1){} //【一元RPC】 }关键性代码服务端代码： @Override public void c2ss(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) { // 接受client客户端的请求参数 String name = request.getName(); // 做业务处理 System.out.println("name = " + name); // 根据业务处理的结果,提供响应 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(); } 客户端代码： package cn.zzrg; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import java.util.Iterator; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient2 { public static void main(String[] args) { // 获得管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build(); try { HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel); HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("ZzRG"); HelloProto.HelloRequest helloRequest = builder.build(); Iterator<HelloProto.HelloResponse> helloResponseIterator = helloService.c2ss(helloRequest); 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(); } } } 监听 异步方式 处理服务流式RPC的开发 1. api 2. 服务端 3. 客户端 package cn.zzrg; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.Iterator; import java.util.concurrent.TimeUnit; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient3 { public static void main(String[] args) { // 获取管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build(); try { HelloServiceGrpc.HelloServiceStub helloService = HelloServiceGrpc.newStub(managedChannel); HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("ZzRG"); HelloProto.HelloRequest helloRequest = builder.build(); helloService.c2ss(helloRequest, new StreamObserver<HelloProto.HelloResponse>() { @Override public void onNext(HelloProto.HelloResponse value) { // 服务端 响应了 一个消息后 需要处理的话 把代码写在这个方法中 System.out.println("服务端每一次响应消息" +value.getResult()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { // 需要把服务 响应的所有数据 拿到后 在进行业务的处理 System.out.println("服务端响应结束 后续可以根据需要 在这里统一处理服务端响应所有的内容"); } }); // 解决相应太快的问题 managedChannel.awaitTermination(5, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); }finally { // 异步编程因为没有阻塞 顺序执行代码太快了 直接shutdown // 服务端响应 发现client 关了 managedChannel.shutdown(); } } } 客户端流式RPC客户端发送多个请求对象，服务端只返回一个结果应用场景IOT(物联网【传感器】) 向服务端 发送数据proto语法rpc cs2s(stream HelloRequest) returns(HelloResponse){}关键性代码1.api rpc cs2s(stream HelloRequest) returns(HelloResponse){} 2.服务端开发 // 客户端 流式RPC 返回值 @Override public StreamObserver<HelloProto.HelloRequest> cs2s(StreamObserver<HelloProto.HelloResponse> responseObserver) { return new StreamObserver<HelloProto.HelloRequest>() { @Override public void onNext(HelloProto.HelloRequest value) { System.out.println("接受到了client发送的一条消息" + value); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("client的所有消息,都发送到了服务器..."); // 提供响应的目的: 当接受了全部的client 提交的信息,并处理后 提供响应 HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder(); builder.setResult("this is result"); HelloProto.HelloResponse helloResponse = builder.build(); responseObserver.onNext(helloResponse); responseObserver.onCompleted(); } }; } 3.客户端开发 package cn.zzrg; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.concurrent.TimeUnit; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient4 { public static void main(String[] args) { // 获取管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build(); try { HelloServiceGrpc.HelloServiceStub helloService = HelloServiceGrpc.newStub(managedChannel); // 监听返回响应 StreamObserver<HelloProto.HelloRequest> helloRequestStreamObserver = helloService.cs2s(new StreamObserver<HelloProto.HelloResponse>() { @Override public void onNext(HelloProto.HelloResponse value) { // 监控响应 System.out.println("服务端 响应 数据内容为" + value.getResult()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("服务端响应结束..."); } }); // 客户端 发送数据 到服务端 多条数据 ,不定时... for (int i = 0; i < 10; i++) { HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder(); builder.setName("ZzRG" + i); HelloProto.HelloRequest helloRequest = builder.build(); helloRequestStreamObserver.onNext(helloRequest); } helloRequestStreamObserver.onCompleted(); // 解决相应太快的问题 managedChannel.awaitTermination(12, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); }finally { // 异步编程因为没有阻塞 顺序执行代码太快了 直接shutdown // 服务端响应 发现client 关了 managedChannel.shutdown(); } } }双向流式RPC客户端可以发送个请求消息，服务端响应多个响应消息 应用场景聊天室proto语法rpc cs2ss(stream HelloRequest) returns(stream HelloResponse){}关键性代码1. api rpc cs2ss(stream HelloRequest) returns(stream HelloResponse){} 2. 服务端开发 //双向流式RPC的客户端开发 @Override public StreamObserver<HelloProto.HelloRequest> cs2ss(StreamObserver<HelloProto.HelloResponse> responseObserver) { return new StreamObserver<HelloProto.HelloRequest>() { @Override public void onNext(HelloProto.HelloRequest value) { System.out.println("接受到了client发送的一条消息" + value.getName()); responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("response" + value.getName() + "value").build()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("接受到了所有请求的消息...."); responseObserver.onCompleted(); } }; } 3. 客户端开发 package cn.zzrg; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.concurrent.TimeUnit; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient5 { public static void main(String[] args) { // 获取管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build(); try { HelloServiceGrpc.HelloServiceStub helloService = HelloServiceGrpc.newStub(managedChannel); // 监听返回响应 StreamObserver<HelloProto.HelloRequest> helloRequestStreamObserver = helloService.cs2s(new StreamObserver<HelloProto.HelloResponse>() { @Override public void onNext(HelloProto.HelloResponse value) { // 监控响应 System.out.println("服务端 响应 数据内容为" + value.getResult()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("服务端响应结束..."); } }); // 客户端 发送数据 到服务端 多条数据 ,不定时... for (int i = 0; i < 10; i++) { HelloProto.HelloRequest helloRequest = HelloProto.HelloRequest.newBuilder().setName("ZzRG" + i).build(); helloRequestStreamObserver.onNext(helloRequest); } helloRequestStreamObserver.onCompleted(); // 解决相应太快的问题 managedChannel.awaitTermination(12, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); }finally { // 异步编程因为没有阻塞 顺序执行代码太快了 直接shutdown // 服务端响应 发现client 关了 managedChannel.shutdown(); } } }4.3.5 gRPC的代理方式1. BlockingStub 阻塞 通信方式 2. Stub 异步 通过监听处理的 3. FutureStub //不常用 同步 异步 NettyFuture 1. FutureStub 只能应用一元RPC第三种方式package cn.zzrg; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import com.zzrg.TestProto; import com.zzrg.TestServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @Author ZzRG * @Date 2023/9/24 17:02 * @Version 1.0 */ public class GrpcClient6 { public static void main(String[] args) { // 1 创建通信的管道 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build(); // 2 获得代理对象 stub try { TestServiceGrpc.TestServiceFutureStub testServiceFutureStub = TestServiceGrpc.newFutureStub(managedChannel); ListenableFuture<TestProto.TestResponse> responseListenableFuture = testServiceFutureStub.testZzRG(TestProto.TestRequest.newBuilder().setResult("this is result").build()); // 同步操作 // TestProto.TestResponse testResponse = responseListenableFuture.get(); // System.out.println(testResponse.getName()); // responseListenableFuture.addListener(()->{ // System.out.println("异步的RPC响应 回来了"); // }, Executors.newCachedThreadPool()); Futures.addCallback(responseListenableFuture, new FutureCallback<TestProto.TestResponse>() { @Override public void onSuccess(TestProto.TestResponse result) { System.out.println("result.getResult" + result.getResult()); } @Override public void onFailure(Throwable t) { } }, Executors.newCachedThreadPool()); System.out.println("后续操作..."); managedChannel.awaitTermination(12, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); }finally { managedChannel.shutdown(); } } }5.gRPC与StringBoot整合5.1gRPC与Stringboot整合的思想1. grpc-server 2. grpc-clientStringBoot与GRPC整个过程中 对于服务端做了什么封装搭建开发环境1. 搭建StringBoot的开发环境 2. 引入GRPC相关内容 cn.zzrg grpc-api 1.0-SNAPSHOT net.devh grpc-server-spring-boot-starter 2.14.0.RELEASE 开发服务端package com.zzrg.service; import com.zzrg.HelloProto; import com.zzrg.HelloServiceGrpc; import io.grpc.stub.StreamObserver; import net.devh.boot.grpc.server.service.GrpcService; /** * @Author ZzRG * @Date 2023/9/25 12:56 * @Version 1.0 */ // 重复多次 @GrpcService public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { @Override public void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) { String name = request.getName(); System.out.println("name is" + name); responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("this is result").build()); responseObserver.onCompleted(); } } //yml 核心配置 gRPC端口 spring: application: name: boot-server # 不启动tomcat main: web-application-type: none grpc: server: port: 9000 客户端环境搭建 net.devh grpc-client-spring-boot-starter 2.14.0.RELEASE 编码1. yml spring: application: name: boot-client grpc: client: grpc-server: address: 'static://127.0.0.1:9000' negotiation-type: plaintext 2. 注入stub @GrpcClient("grpc-server") private HelloServiceGrpc.HelloServiceBlockingStub stub; 3. 引入 @RequestMapping("/test") public String test(String name) { System.out.println("name = " + name); HelloProto.HelloResponse helloResponse = stub.hello(HelloProto.HelloRequest.newBuilder().setName(name).build()); return helloResponse.getResult(); }gRPC的高级应用1. 拦截器 一元拦截器 2. Stram Tracer【监听流】 流拦截器 解决后三种通讯方式 3. Retry Policy 客户端重试 4. NameResolver 5. 负载均衡 （pick-first ，轮询） 6. grpc与微服务的整合 序列化 （protobuf）Dobbo grpc Dobbo grpc GateWay grpc JWT grpc Nacos2.0 grpc OpenFeign