Spring + Grpc + NacOS 集成
使用Grpc之前需要了解的
Grpc
Grpc与其他普通Rpc相同,都是屏蔽掉远程调用之间的细节,使得远程调用与本地调用一般,HTTP/2 是Grpc的默认使用协议。
HTTP/2与HTTP/1.x
HTTP/1.x是一个文本传输协议,可读性非常好。HTTP/2是一个二进制协议,所有的数据传输并不易读。

Wireshark可以帮我们解析
下面这个是OpenFeign,基于HTTP/1.1


具体HTTP/1.x和HTTP/2的优缺点可以移步至
Protocol Buffers
ProtoBuf 是google团队开发的用于高效存储和读取结构化数据的工具,你可以理解为另一种格式的JSON,正是因为如此,Java中普通的JSON序列化和反序列化工具对其不起作用,需要使用到
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java-util</artifactId> </dependency>
|
来针对JSON和ProtoBuf相互转换
proto文件
类似的,你会看到和下面文件相似的
syntax = "proto3";
option java_multiple_files = true; option java_package = "cn.bwte.grpc"; option java_outer_classname = "TestProto";
service Test { rpc SayHello (HelloRequest) returns (HelloReply) { }
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
|
可以理解为,定义了proto的协议版本,需要生成的javaClass包名和类名,还有定义的方法调用以及实体。
那我们怎么才能让这玩意生成Java实体呢
<plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>${protobuf-plugin.version}</version> <configuration> <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins>
|
Maven是个好东西,添加了这个之后,在每次的install都会根据proto生成你定义的实体
理论存在 实践开始
准备阶段
技术选型
目前Spring官方也出了对于Grpc的调用组建,合并在全家桶中,但是对于Java和Spring的要求比较高,需要 Spring Boot 3.4.x and 3.5.x,感兴趣的同学可以Getting Started
本文采用的是grpc-spring,一个第三方Grpc的实现
Grpc接口定义
<properties> <protobuf.version>3.23.4</protobuf.version> <protobuf-plugin.version>0.6.1</protobuf-plugin.version> <grpc.version>1.58.0</grpc.version> </properties> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> </dependencies> 插件引入上文说的那个Maven插件,需要用来生成
|
定义proto
syntax = "proto3";
option java_multiple_files = true; option java_package = "cn.ashes.grpc"; option java_outer_classname = "CustomerProto";
service Customer { rpc queryCustomerInfo (CustomerRequest) returns (CustomerReplyList) { } } message CustomerReplyList { repeated CustomerReply replies = 1; }
message CustomerReply { int64 id = 1; string customerCode = 2; string customerName = 3; }
message CustomerRequest { string customerName = 1; int32 pageNo = 2; int32 pageSize = 3; }
|
在进行install之后,就会生成对应的GrpcService与实体
服务端实现
实现Service
@GrpcService public class CustomerService extends CustomerGrpc.CustomerImplBase { @Resource private CustomerController customerController;
@Override public void queryCustomerInfo(CustomerRequest request, StreamObserver<CustomerReplyList> responseObserver) { String json; try { json = JsonFormat.printer().print(request); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } CustomerInfoQuery bean = JSONUtil.toBean(json, CustomerInfoQuery.class); CustomerInfo customerInfoJsonResult = customerController.queryCustomerInfo(bean); CustomerReplyList.Builder listBuilder = CustomerReplyList.newBuilder();
customerInfoJsonResult.getData().forEach(customerInfo -> { String json1 = JSONUtil.toJsonStr(customerInfo); CustomerReply.Builder builder = CustomerReply.newBuilder();
try { JsonFormat.parser().ignoringUnknownFields().merge(json1, builder); listBuilder.addReplies(builder.build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } }); responseObserver.onNext(listBuilder.build()); responseObserver.onCompleted(); } }
|
服务端事例为调用查询客户信息
简单的测试下叭
grpcurl --plaintext localhost:9099 list Customer
grpcurl --plaintext -d '{"customerName": "喵喵"}' localhost:9099 Customer.queryCustomerInfo
|
你说你没有grpcurl
?指路grpcurl
你要是有HomeBrew就更爽了直接brew install grpcurl
理想情况你就会得到正确的反差了
客户端实现
但是我想用其他微服务来调用怎么办捏
注册服务到注册中心会把?那我不教了
下一步就是写客户端
依赖注入
@GrpcClient("Grpc的Service在注册中心的名字") private CustomerGrpc.CustomerBlockingStub customerBlockingStub;
|
这里我们用Block的实现来做事例,Grpc给我们三种实现NewBlockingStub 、 newStub 、 newFutureStub
分别代表阻塞调用,纯异步调用客户端,异步带Future调用
CustomerRequest build = CustomerRequest.newBuilder() .setCustomerName("喵喵") .setPageNo(1) .setPageSize(100).build(); CustomerReplyList customerReplyList = customerBlockingStub.queryCustomerInfo(build);
|
直接调用就可以了
延展阅读
- Grpc
- HTTP/2对比HTTP/1.1,特性是什么?是如何解决队头阻塞与压缩头部的?
- 详解HTTP协议版本(HTTP/1.0、1.1、2.0、3.0区别)
- Protocol Buffer 基础知识:Java
- grpc-spring
- Getting Started
- grpcurl
- gRPC三种客户端类型实践【Java版】