gRPC
why gRPC?
gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
gRPC is an open source, high-performance RPC framework, which can run in any environment. It can effectively connect data center internal services and across (I understand it as connecting data centers) data center services, and support pluggable load balancing, link tracking, health checking and privilege validation. It can also run in any environment. In the last step of application distributed computing, devices, mobile applications and browsers are connected to back-end services.
gRpc uses protobuf as the interface definition language (IDL).
service HelloService { rpc SayHello (HelloRequest) returns (HelloResponse); } message HelloRequest { string greeting = 1; } message HelloResponse { string reply = 1; }
gRPC allows four service methods to be defined:
1. Univariate rpc, the client sends a request to the server and gets a response, just like a normal function call.
rpc SayHello(HelloRequest) returns (HelloResponse){ }
2. Server stream rpc, client sends request to server and gets stream to read message sequence. The client reads from the returned stream until there are no more messages. grpc guarantees message sorting in a single RPC call.
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){ }
3, and 2, on the contrary, are the normal body of messages returned by the flow upon request
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) { }
In combination of 4, 2, and 3, requests and returns are streams
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){ }
gRPC integrates gradle generation code.
1. First go to the gRPC github website https://github.com/grpc/grpc-java and introduce gradle dependency according to the instructions in readme.
"io.grpc:grpc-netty-shaded:1.23.0", "io.grpc:grpc-protobuf:1.23.0", "io.grpc:grpc-stub:1.23.0"
2. Generated Code code generation. Place proto files in src/main/proto and src/test/proto directories, and add the following plug-ins below is the build.gradle file.
group 'com.fjh' version '1.0' apply plugin: 'java' apply plugin: 'com.google.protobuf' sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { maven { url "https://maven.aliyun.com/nexus/content/groups/public" } mavenCentral() } dependencies { compile( "io.netty:netty-all:4.1.0.Final", "com.google.protobuf:protobuf-java:3.9.1", "com.google.protobuf:protobuf-java-util:3.9.1", "org.apache.thrift:libthrift:0.12.0", "io.grpc:grpc-netty-shaded:1.23.0", "io.grpc:grpc-protobuf:1.23.0", "io.grpc:grpc-stub:1.23.0" ) } buildscript { repositories { mavenCentral() } dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10' } } protobuf { protoc { artifact = "com.google.protobuf:protoc:3.9.0" } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.23.0' } } generateProtoTasks.generatedFilesBaseDir = "src" generateProtoTasks { all()*.plugins { grpc { setOutputSubDir 'java' } } } }
Examples of GRPC use are as follows:
First, the proto file is defined:
syntax = "proto3"; package com.fjh.netty.grpc; option java_package = "com.fjh.netty.grpc"; option java_outer_classname = "StudentProto"; //Whether to generate multiple files option java_multiple_files = true; service StudentService { rpc GetRealNameByUsername(MyRequest) returns (MyResponse){} rpc GetStudentsByAge(StudentRequest) returns (stream StudentResponse){} rpc GetStudentsWrapperByAges(stream StudentRequest) returns (StudentResponseList){} rpc BiTalk(stream StreamRequest) returns (stream StreamResponse){} } message MyRequest { string username = 1; } message MyResponse { string realname = 1; } message StudentRequest { string name = 1; int32 age = 2; string city = 3; } message StudentResponse { string name = 1; int32 age = 2; string city = 3; } message StudentResponseList { repeated StudentResponse studentResponse = 1; } message StreamRequest { string request_info = 1; } message StreamResponse { string response_info = 1; }
Then execute the gradle generator Proto command to generate the corresponding java file. You can also quickly execute commands directly on the ide.
The implementation class of service.
public class StudentServiceImpl extends StudentServiceGrpc.StudentServiceImplBase { @Override public void getRealNameByUsername(MyRequest request, StreamObserver<MyResponse> responseObserver) { System.out.println("Receive client parameters:"+ request.getUsername()); responseObserver.onNext(MyResponse.newBuilder().setRealname("Zhang San").build()); responseObserver.onCompleted(); } @Override public void getStudentsByAge(StudentRequest request, StreamObserver<StudentResponse> responseObserver) { System.out.println("Receive client parameters:"+ request.getAge()); responseObserver.onNext(StudentResponse.newBuilder().setName("Zhang San").setAge(10).setCity("Beijing").build()); responseObserver.onNext(StudentResponse.newBuilder().setName("Li Si").setAge(20).setCity("Shanghai").build()); responseObserver.onCompleted(); } @Override public StreamObserver<StudentRequest> getStudentsWrapperByAges(StreamObserver<StudentResponseList> responseObserver) { return new StreamObserver<StudentRequest>() {; @Override public void onNext(StudentRequest value) { System.out.println("onNext:"+value.getAge()); } @Override public void onError(Throwable t) { System.out.println(t.getMessage()); } @Override public void onCompleted() { StudentResponse studentResponse1 = StudentResponse.newBuilder().setName("Zhang San").setAge(10).setCity("Beijing").build(); StudentResponse studentResponse2 = StudentResponse.newBuilder().setName("Li Si").setAge(20).setCity("Shanghai").build(); StudentResponseList studentResponseList = StudentResponseList.newBuilder().addStudentResponse(studentResponse1) .addStudentResponse(studentResponse2).build(); responseObserver.onNext(studentResponseList); responseObserver.onCompleted(); } }; } @Override public StreamObserver<StreamRequest> biTalk(StreamObserver<StreamResponse> responseObserver) { return new StreamObserver<StreamRequest>() { @Override public void onNext(StreamRequest value) { System.out.println(value.getRequestInfo()); responseObserver.onNext(StreamResponse.newBuilder().setResponseInfo(UUID.randomUUID().toString()).build()); } @Override public void onError(Throwable t) { System.out.println(t.getMessage()); } @Override public void onCompleted() { responseObserver.onCompleted(); } }; } }
GRPC server:
public class GRpcServer { private Server server; // Service startup private void start() throws IOException { this.server = ServerBuilder.forPort(8899).addService(new StudentServiceImpl()).build().start(); System.out.println("server started"); // jvm callback hook closes connection when jvm process ends Runtime.getRuntime().addShutdownHook(new Thread(()->{ System.out.println("Close jvm"); GRpcServer.this.stop(); })); System.out.println("Execute here"); } // Service closure private void stop(){ if (this.server != null){ this.server.shutdown(); } } // Waiting for termination means blocking the service and waiting for the connection private void awaitTermination() throws InterruptedException { if (this.server != null){ this.server.awaitTermination(); } } public static void main(String[] args) throws IOException, InterruptedException { GRpcServer server = new GRpcServer(); server.start(); // If this line of code does not start, it exits directly. server.awaitTermination(); } }
GRPC Client Calls service Method
public class GRpcClient { public static void main(String[] args) { ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",8899) .usePlaintext().build(); // Blocked stub StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc .newBlockingStub(managedChannel); // Asynchronous stub StudentServiceGrpc.StudentServiceStub stub = StudentServiceGrpc.newStub(managedChannel); // Client calls the first method System.out.println("===============Call the first method============"); MyResponse myResponse = blockingStub.getRealNameByUsername(MyRequest.newBuilder().setUsername("zhangsan").build()); System.out.println(myResponse.getRealname()); blockingStub.getRealNameByUsername(MyRequest.newBuilder().setUsername("zhangsan").build()); System.out.println("===========Call the second method===================="); // Client calls the second method Iterator<StudentResponse> studentsByAge = blockingStub.getStudentsByAge(StudentRequest.newBuilder().setAge(10).build()); studentsByAge.forEachRemaining(studentResponse -> { System.out.println(studentResponse.getName()); System.out.println(studentResponse.getAge()); System.out.println(studentResponse.getCity()); }); // Calling the third method and the third method uses stub System.out.println("=========Call the third method==============="); StreamObserver<StudentResponseList> responseListStreamObserver = new StreamObserver<StudentResponseList>() { @Override public void onNext(StudentResponseList value) { value.getStudentResponseList().forEach( studentResponse -> { System.out.println( studentResponse.getName()+","+ studentResponse.getAge() +","+studentResponse.getCity()); }); } @Override public void onError(Throwable t) { System.out.println(t); } @Override public void onCompleted() { System.out.println("onCompleted"); } }; StreamObserver<StudentRequest> studentsWrapperByAges = stub.getStudentsWrapperByAges(responseListStreamObserver); studentsWrapperByAges.onNext(StudentRequest.newBuilder().setAge(10).getDefaultInstanceForType()); studentsWrapperByAges.onNext(StudentRequest.newBuilder().setAge(20).getDefaultInstanceForType()); studentsWrapperByAges.onCompleted(); // The third method needs to wait here, because it is the result of the flow. sleep here, otherwise it will not be the result. try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // Call the fourth method System.out.println("=================Call the fourth method================"); StreamObserver<StreamRequest> requestStreamObserver = stub.biTalk(new StreamObserver<StreamResponse>() { @Override public void onNext(StreamResponse value) { System.out.println(value.getResponseInfo()); } @Override public void onError(Throwable t) { System.out.println(t.getMessage()); } @Override public void onCompleted() { System.out.println("onCompleted"); } }); for (int i = 0; i < 10 ; i++) { requestStreamObserver.onNext(StreamRequest.newBuilder().setRequestInfo(LocalDateTime.now().toString()).build()); } // The third method needs to wait here, because it is the result of the flow. sleep here, otherwise it will not be the result. try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // //managedChannel.shutdown(); } }