说明文档:http://doc.oschina.net/grpc?t=60134
参考文档:https://zhuanlan.zhihu.com/p/377860784
参考文档:https://baijiahao.baidu.com/s?id=1762269686952396195&wfr=spider&for=pc
依赖 1 2 3 4 5 <dependency > <groupId > net.devh</groupId > <artifactId > grpc-server-spring-boot-starter</artifactId > <version > 2.14.0.RELEASE</version > </dependency >
1 2 3 4 5 <dependency > <groupId > net.devh</groupId > <artifactId > grpc-client-spring-boot-starter</artifactId > <version > 2.14.0.RELEASE</version > </dependency >
如果服务只作为grpc服务端或者grpc客户端,建议只引入对应的依赖,不要引入grpc-spring-boot-starter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.5.1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}</pluginArtifact> <!--设置proto文件目录--> <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot> <!--设置grpc生成代码到指定路径--> <outputDirectory>${project.basedir}/src/main/java</outputDirectory> <!--生成代码前是否清空目录--> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
os-maven-plugin和protobuf-maven-plugin都需要
配置 1 2 3 4 5 server.port =8080 spring.application.name =grpc-server grpc.server.port =9090
1 2 3 4 5 6 7 8 9 10 11 grpc.client.grpc-server.address =static://localhost:9090 grpc.client.grpc-server.enableKeepAlive =true grpc.client.grpc-server.keepAliveWithoutCalls =true grpc.client.grpc-server.negotiationType =plaintext spring.application.name =grpc-client server.port =8081
编写proto文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 syntax = "proto3" ; option java_multiple_files = true ;option java_package = "com.example.client.proto" ;service EventInfoService { rpc sendMessageEvent(EventInfoMessage) returns (EventInfoResponse) {} } message EventInfoMessage { string msg = 1 ; } message EventInfoResponse { int32 code = 1 ; string msg = 2 ; bool success = 3 ; }
proto文件编写还比较简单
service EventInfoService:代表要创建一个名为EventInfoService的类,并且具有sendMessageEvent方法,这个方法需要指定入参和反参,指定的入参和反参都要此文件中或者引入的其他proto文件中定义,这个方法代表了访问服务端的接口,所以proto文件服务端与客户端要一致,生成的代码中会有三个不同的io.grpc.stub.AbstractStub实现类和一个io.grpc.BindableService的实现类,前者是为客户端实现的,客户端调用请求方法在前者中实现的,后者是为了服务端实现的,实现了服务端接收请求的方法,四个实现类都会实现自定义的请求方法
XXXServiceBlockingStub:代表阻塞的实现我们定义的方法(例如上面的sendMessageEvent),即发送消息的方法会阻塞直到客户端接收到服务端的响应,即上面的EventInfoResponse
XXXServiceFutureStub:代表等待的实现我们定义的方法,即发送消息后会立即返回一个com.google.common.util.concurrent.ListenableFuture<自定义返回参数>,但是调用ListenableFuture的get方法时会阻塞
XXXServiceStub:代表完全异步的实现我们定义的方法,即发送消息时,需要我们传入一个回调方法并且不会返回参数,回调方法实现io.grpc.stub.StreamObserver<自定义返回参数>即可
XXXServiceImplBase:实现了处理请求的方法
message EventInfoMessage:代表创建一个名为EventInfoMessage的消息类
message EventInfoResponse:代表创建一个名为EventInfoResponse的消息类
通过proto生成代码
执行完就可以在指定目录看到代码了
客户端请求代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @RestController public class ClientController { @GrpcClient("grpc-server") private EventInfoServiceGrpc.EventInfoServiceBlockingStub eventInfoServiceBlockingStub; @GrpcClient("grpc-server") private EventInfoServiceGrpc.EventInfoServiceFutureStub eventInfoServiceFutureStub; @GrpcClient("grpc-server") private EventInfoServiceGrpc.EventInfoServiceStub eventInfoServiceStub; @RequestMapping(value = "/query/{str}") public String queryUser (@PathVariable String str) throws ExecutionException, InterruptedException { EventInfoResponse eventInfoResponse = eventInfoServiceBlockingStub.sendMessageEvent(EventInfoMessage.newBuilder().setMsg("success" ).build()); System.out.println(eventInfoResponse.getMsg()); ListenableFuture<EventInfoResponse> eventInfoResponseListenableFuture = eventInfoServiceFutureStub.sendMessageEvent(EventInfoMessage.newBuilder().setMsg("success" ).build()); System.out.println(eventInfoResponseListenableFuture.get().getMsg()); eventInfoServiceStub.sendMessageEvent(EventInfoMessage.newBuilder().setMsg("success" ).build(), new StreamObserver <EventInfoResponse>() { @Override public void onNext (EventInfoResponse response) { System.out.println(response.getMsg()); } @Override public void onError (Throwable throwable) { new Exception (throwable).printStackTrace(); } @Override public void onCompleted () { System.out.println("complete" ); } }); return "success" ; } }
这里展示三个AbstractStub实现类的用法示例,客户端会使到io.grpc.ClientCall类
服务端响应代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @GrpcService public class EventInfoServiceGrpcImpl extends EventInfoServiceGrpc .EventInfoServiceImplBase { @Override public void sendMessageEvent (EventInfoMessage request, StreamObserver<EventInfoResponse> responseObserver) { EventInfoResponse.Builder eventInfo = EventInfoResponse.newBuilder(); String msg = request.getMsg(); if ("success" .equals(msg)){ eventInfo.setCode(200 ).setMsg("success" +100000 ).setSuccess(true ); }else { eventInfo.setCode(500 ).setMsg("error" +100000 ).setSuccess(false ); } try { Thread.sleep(10000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } responseObserver.onNext(eventInfo.build()); responseObserver.onCompleted(); } }
服务端请求响应处理类需要继承BindableService的实现类并实现我们自定义方法名称的方法,服务端使用了ServerCall