上篇文章介绍了如何通过一个简单的maven项目实现gRPC客户端与服务端通信协议测试。但是这个只是一个普通的Java工程,我们在Java web开发的今天大多数Java程序员都是通过SpringCloud及SpringBoot开启微服务或者单个应用的开发,我们最好通过springboot继承gRPC,这样一个工程既可以支持web开发,也可以支持gRPC协议通信。庆幸的是有大佬yidongnan早为我们准备好了grpc相关starter,对应的开源代码地址如下:
https://github.com/yidongnan/grpc-spring-boot-starter
grpc-client-spring-boot-starter:client端start,实现自动注入stub的实现
grpc-client-spring-boot-autoconfigure:client端grpc Springboot配置文件自动加载注入
grpc-server-spring-boot-starter:server端启动starter,实现自动注入XXXServiceGrpc
grpc-server-spring-boot-autoconfigure:server端grpc springboot配置文件自动加载注入
这里是大佬yidongnan维护的项目:gRPC-Spring-Boot-Starter 文档及源码
gRPC-Spring-Boot-Starter 文档 | grpc-spring
具体server端实现和client端实现完全可以参考大佬的examples,完全足够让你理解其实现的便捷性,你也可以按照其参考实现,提供maven和gradle两种构建方式。如果你在看的过程中还有些许不懂,下面是我按照自己的理解把之前gRPC逻辑重新实现了一下,看这篇文章之前,建议先看一下我之前的一篇文章:
【JAVA】protobuf在Java中代码测试用例-CSDN博客
我们主要做以下工作
创建一个父项目(springboot-rpc),包括以下几个module
- 定义服务调用接口程序(rpc-interface),一个普通工程
- 创建server端Springboot服务(rpc- server),依赖接口程序
- 创建client端Springboot服务(rpc-client),依赖接口程序
一、创建父项目
1.父项目pom.xml,在springboot-rpc工程配置子module需要用到的maven依赖
org.examplespringboot-rpc1.0-SNAPSHOTpomrpc-interfacerpc-serverrpc-clientUTF-81.59.13.24.03.24.01.81.8io.grpcgrpc-netty-shaded${grpc.version}runtimeio.grpcgrpc-protobuf${grpc.version}io.grpcgrpc-services${grpc.version}io.grpcgrpc-stub${grpc.version} org.apache.tomcatannotations-api6.0.53providedcom.google.protobufprotobuf-java${protobuf.version}com.google.protobufprotobuf-java-util${protobuf.version}org.apache.maven.pluginsmaven-compiler-plugin3.8.11.81.8UTF-8
二、定义接口程序
1.定义接口定义程序rpc-interface maven依赖配置pom.xml
org.examplespringboot-rpc1.0-SNAPSHOTrpc-interfaceUTF-81.81.8io.grpcgrpc-protobuf<!--1.59.1-->io.grpcgrpc-stub<!--1.59.1-->com.google.protobufprotobuf-javacom.google.protobufprotobuf-java-utiljakarta.annotationjakarta.annotation-api1.3.5truekr.motd.mavenos-maven-plugin1.7.1org.codehaus.mojobuild-helper-maven-plugin3.3.0testgenerate-sourcesadd-source${project.basedir}/target/generated-sourcescom.github.os72protoc-jar-maven-plugin3.11.4generate-sourcesrunprotoctrue${protobuf.version}true<!-- 导入其他proto文件src/main/more_proto_imports-->src/main/protobufjavagrpc-javaio.grpc:protoc-gen-grpc-java:1.48.1src/main/java
2.定义proto文件
生成源码需要依赖proto文件,文件内容如下:
syntax="proto3";option go_package="./;student"; //关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,student代表了生成的go文件的包名是studentoption java_multiple_files = true; //表示下面的message需要编译成多个java文件option java_package = "grpc.student"; //指定该proto文件编译成的java源文件的包名option java_outer_classname = "StudentProto"; // 表示下面的message编译成的java类文件的名字package student; // 定义作用域service DemoService {rpc Sender(StudentRequest) returns (StudentResponse){}}message StudentRequest {string Id = 1;}message StudentResponse {bytes result =1;}message Student {int64 Id = 1;string Name =2;string No =3;}
3.生成源码程序
依赖student.proto文件,执行编译生成源码文件,执行如下命令
mvn clean compile #当前rpc-interface工程执行
将生成的源码拷贝到src/main/grpc/student包下,工程结构如下:
rpc-interface├── pom.xml└── src├── main│ ├── java│ │ └── grpc│ │ └── student│ │ ├── DemoServiceGrpc.java│ │ ├── Student.java│ │ ├── StudentOrBuilder.java│ │ ├── StudentProto.java│ │ ├── StudentRequest.java│ │ ├── StudentRequestOrBuilder.java│ │ ├── StudentResponse.java│ │ └── StudentResponseOrBuilder.java│ ├── protobuf│ │ └── student.proto│ └── resources
三、创建gRPC Server服务
server端为了能依赖spring自动注入我们需要的Service实现,需要加入依赖grpc-server-spring-boot-starter,此starter自动根据springboot配置文件自动注入我们需要的service。
1.rpc-server pom.xml配置如下
org.examplespringboot-rpc1.0-SNAPSHOTrpc-servernet.devhgrpc-server-spring-boot-starter2.15.0.RELEASEorg.springframework.bootspring-boot-starter-web2.7.16org.examplerpc-interface1.0-SNAPSHOT
2.rpc-server配置文件application.yml
spring:application:name: student-rpc-server#rpc client根据此名称配置server:servlet:context-path: /port: 9999#grpc server端暴露的端口grpc:server:port: 10005
3.编写server端的接口实现
在rpc- interface中定义的接口sender()方法需要在这里实现,并标注@GrpcService注解,代码如下:
@GrpcServicepublic class RpcServerImpl extends DemoServiceGrpc.DemoServiceImplBase {private Logger log = LoggerFactory.getLogger(RpcServerImpl.class.getName());@Overridepublic void sender(StudentRequest request, StreamObserver responseObserver) {if (Strings.isNullOrEmpty(request.getId())){log.warn("request get param id is null");return;}int id = Integer.parseInt(request.getId());//build a student object then serialize it//Student student = Student.newBuilder().build().;Student.Builder builder = Student.newBuilder();builder.setId(id);builder.setName("easton");builder.setNo("10001");Student student = builder.build();//try catch 属于测试序列化与反序列化代码块ByteString jsonBs = null;try {//protobuf 序列化String jsonStr = JsonFormat.printer().print(student);log.info("json format:"+jsonStr);jsonBs =ByteString.copyFromUtf8(jsonStr);//反序列化byte[] bytes = student.toByteArray();Student student1 = Student.parseFrom(bytes);} catch (InvalidProtocolBufferException e) {throw new RuntimeException(e);}//ByteString bs =student.toByteString();StudentResponse response = StudentResponse.newBuilder().setResult(jsonBs).build();responseObserver.onNext(response);//需要告诉客户端数据写完,否则客户端会一直等待数据回传结束responseObserver.onCompleted();log.info("server method runfinish");}}
4.编写rpc-server启动类
@SpringBootApplicationpublic class BootStrarpApplication {public static void main(String[] args) {SpringApplication.run(BootStrarpApplication.class,args);}}
四、创建gRPC client服务
创建完server端后接着编写客户端springboot服务
1.rpc-client pom.xml配置
跟服务端相对应需要引入依赖:grpc-client-spring-boot-starter,此starter会自动注入客户端stub实现,依赖配置为:
org.examplespringboot-rpc1.0-SNAPSHOTrpc-clientnet.devhgrpc-client-spring-boot-starter2.15.0.RELEASEorg.springframework.bootspring-boot-starter-web2.7.16org.examplerpc-interface1.0-SNAPSHOT
2.rpc-client stub注入的代码调用实现
定义方法,此方法用来gRPC调用服务端sender()方法
@Servicepublic class StudentClientService {//初始化student-rpc-server对应的stub,如果需要多个可以在这里注入@GrpcClient("student-rpc-server")private DemoServiceGrpc.DemoServiceBlockingStub blockingStub;Logger logger = LoggerFactory.getLogger(StudentClientService.class);public String sendToServer(int id){logger.info("Will try to send " + id + " ...");StudentRequest request = StudentRequest.newBuilder().setId(String.valueOf(id)).build();StudentResponse response;try{response = blockingStub.sender(request);}catch (StatusRuntimeException e){e.printStackTrace();logger.warn("RPC failed: {0}", e.getStatus());return "";}ByteString byteString = response.getResult();String result = byteString.toStringUtf8();logger.info("Result: " +result);return result;}}
3.rpc-client启动类
@SpringBootApplicationpublic class BootStrapApplication {public static void main(String[] args) {SpringApplication.run(BootStrapApplication.class,args);}}
4.rpc-client 配置文件application.yml配置
server:port: 8080servlet:context-path: /spring:application:name: student-rpc-clientgrpc:client:student-rpc-server: #服务名称不能写错,这个需要和server端spring.application.name的定义的名称一致,否默认的negotiationType:tlsaddress: 'static://localhost:10005'# 是否开启保持连接(长连接)enableKeepAlive: true# 保持连接时长(默认20s)keepAliveTimeout: 20s# 没有RPC调用时是否保持连接(默认false,可禁用避免额外消耗CPU)keepAliveWithoutCalls: false# 客户端负载均衡策略(round_robin(默认), pick_first)defaultLoadBalancingPolicy: round_robin# 通信类型# plaintext | plaintext_upgrade | tls# 明文通信且http/2 | 明文通信且升级http/1.1为http/2 | 使用TLS(ALPN/NPN)通信negotiationType: plaintext#GLOBAL: 可以指定所有grpc通用配置
5.定义一个测试接口
由于rpc- client开启了web服务,为了模拟接口调用,以Restful协议请求rpc-client http接口,http接口调用grpc server服务,创建接口如下:
@RestControllerpublic class DemoClientController {@Autowiredprivate StudentClientService clientService;@RequestMapping(value = "/getResult",method = RequestMethod.GET)public String getResult(@RequestParam("id") String id){returnclientService.sendToServer(Integer.valueOf(id));}}
五、调用测试
1.启动服务端rpc-server
2.启动客户端rpc-client
3.通过浏览器URL请求测试:http://localhost:8080/getResult?id=111
{ "Id": "111", "Name": "easton", "No": "10001" }