gRPC implementation
With gRPC, we can define services once in a. proto file and generate clients and servers in any language supported by gRPC, which can run in a variety of environments from servers to computers in large data centers – gRPC handles different languages and environments for you. It also obtains all the advantages of using protocol buffers, including effective serialization, simple IDL and easy interface update.
There are four service types in grpc:
Simple rpc: This is a general rpc call. A request object corresponds to a return object
Server streaming rpc: a request object. The server can return multiple result objects
Client streaming rpc: the client passes in multiple request objects, and the server returns a response result
Bidirectional streaming rpc: combining client streaming rpc and server streaming rpc, multiple objects can be passed in and multiple response objects can be returned
grpc returns / accepts multiple instances in a streaming manner, which can be used for input and output parameters similar to indefinite length arrays
preparation
Create maven project grpc and generate proto interface project grpc PROTOS
Grpc PROTOS project
Create the interface file route under grpc PROTOS_ Guide.proto, as follows
// Using proto3 syntax syntax = "proto3"; // Generate multiple classes option java_multiple_files = true; // Generate the package where the java class is located option java_package = "io.grpc.examples.routeguide"; // Generate outer class class name option java_outer_classname = "RouteGuideProto"; // Prefix of Objective-C class option objc_class_prefix = "RTG"; // proto package name package routeguide; // Define RPC service RouteGuide service RouteGuide { // Simple RPC accepts the simplerrpcreq parameter and returns an object of type simplerrpcres rpc GetFeature(SimpleRpcReq) returns (SimpleRpcRes) {} // Server to client streaming RPC, accept ServerToClientStreamRpcReq object parameters and return batch ServerToClientStreamRpcRes data rpc ListFeatures(ServerToClientStreamRpcReq) returns (stream ServerToClientStreamRpcRes) {} // Client to server streaming RPC, accept batch Point data, and return RouteSummary type objects rpc RecordRoute(stream ClientToServerStreamRpcReq) returns (ClientToServerStreamRpcReq) {} // Bidirectional streaming RPC, which accepts batch RouteNote type data and returns batch RouteNote type data rpc RouteChat(stream TwoWayStreamRpcReq) returns (stream TwoWayStreamRpcRes) {} } /** Simple RPC**/ // Point message SimpleRpcReq { int32 latitude = 1; int32 longitude = 2; } // Feature message SimpleRpcRes { string name = 1; Point location = 2; } /** Simple RPC**/ /** Server to client streaming RPC**/ message ServerToClientStreamRpcReq { Point lo = 1; Point hi = 2; } // Feature message ServerToClientStreamRpcRes { string name = 1; Point location = 2; } /** Server to client streaming RPC**/ /** Client to server streaming RPC**/ // Point message ClientToServerStreamRpcReq { Point lo = 1; Point hi = 2; } message ClientToServerStreamRpcRes { int32 point_count = 1; int32 feature_count = 2; int32 distance = 3; int32 elapsed_time = 4; } /** Client to server streaming RPC**/ /** Client to server streaming RPC**/ message TwoWayStreamRpcReq { Point location = 1; string message = 2; } message TwoWayStreamRpcRes { Point location = 1; string message = 2; } /** Client to server streaming RPC**/ message Point { int32 latitude = 1; int32 longitude = 2; } message Feature { string name = 1; Point location = 2; }
grpc project
pom file contents are as follows
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.starnet</groupId> <artifactId>grpc</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>11</java.version> <grpc.version>1.29.0</grpc.version> <protobuf.version>3.11.0</protobuf.version> </properties> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <!-- necessary for Java 9+ --> <groupId>org.apache.tomcat</groupId> <artifactId>annotations-api</artifactId> <version>6.0.53</version> <scope>provided</scope> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</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> <protoSourceRoot>../grpc-protos</protoSourceRoot> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Note that the protoSourceRoot configured in the protobuf plug-in is the absolute path or relative path of the protos interface directory, because the plug-in needs to find the proto file according to the directory to generate relevant code; If protoSourceRoot is not set, it defaults to the src/main/proto directory of the project
Generate grpc protobuf related classes
Execute in grpc directory or generate directly through Idea
mvn protobuf:compile mvn protobuf:compile-custom
The corresponding code is generated in the target directory
code implementation
Server
Start gRPC Server
import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; import java.util.concurrent.TimeUnit; public class GRpcServer { private Server server; private void start() throws IOException { /* The port on which the server should run */ int port = 50051; server = ServerBuilder.forPort(port) .addService(new RouteGuideService()) //Multiple modules can be added here .build() .start(); System.out.println("Server started, listening on " + port); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { // Use stderr here since the logger may have been reset by its JVM shutdown hook. System.err.println("shutting down gRPC server since JVM is shutting down"); try { GRpcServer.this.stop(); } catch (InterruptedException e) { e.printStackTrace(System.err); } System.err.println("server shut down"); } }); } private void stop() throws InterruptedException { if (server != null) { server.shutdown().awaitTermination(30, TimeUnit.SECONDS); } } private void blockUntilShutdown() throws InterruptedException { if (server != null) { server.awaitTermination(); } } public static void main(String[] args) throws IOException, InterruptedException { GRpcServer server = new GRpcServer(); server.start(); server.blockUntilShutdown(); } }
gRPC server processing
import io.grpc.examples.routeguide.*; import io.grpc.stub.StreamObserver; import java.util.ArrayList; import java.util.List; public class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase { /** * Simple RPC accepts the simplerrpcreq parameter and returns an object of type simplerrpcres * @param request * @param responseObserver */ @Override public void getSimpleRpcRes(SimpleRpcReq request, StreamObserver<SimpleRpcRes> responseObserver) { Point location = Point.newBuilder() .setLatitude(request.getLatitude()) .setLongitude(request.getLongitude()) .build();; SimpleRpcRes simpleRpcRes = SimpleRpcRes .newBuilder() .setName("fuzhou") .setLocation(location) .build(); // Send a message to the client through responseObserver.onNext responseObserver.onNext(simpleRpcRes); // Mark the completion of server response responseObserver.onCompleted(); } /** * Server to client streaming RPC, accept ServerToClientStreamRpcReq object parameters and return batch ServerToClientStreamRpcRes data * @param request * @param responseObserver */ @Override public void listServerToClientStreamRpcRes(ServerToClientStreamRpcReq request, StreamObserver<ServerToClientStreamRpcRes> responseObserver) { List<Point> pointList = new ArrayList<>(); pointList.add(request.getLo()); pointList.add(request.getHi()); int i = 1; for (Point point : pointList) { ServerToClientStreamRpcRes res = ServerToClientStreamRpcRes.newBuilder() .setName("fuzhou" + i++) .setLocation(point) .build(); // Call responseObserver.onNext in a loop to send data to the client callback responseObserver.onNext(res); } responseObserver.onCompleted(); } /** * Client to server streaming RPC, accept batch Point data, and return RouteSummary type objects * @param responseObserver * @return */ @Override public StreamObserver<ClientToServerStreamRpcReq> getClientToServerStreamRpcRes(StreamObserver<ClientToServerStreamRpcRes> responseObserver) { // Construct observer client interaction return new StreamObserver<ClientToServerStreamRpcReq>() { int pointCount; // Response client @Override public void onNext(ClientToServerStreamRpcReq point) { System.out.println("point is " + point); pointCount++; } @Override public void onError(Throwable t) { System.out.println("client to server stream rpc is cancelled"); } // Triggered when the client calls requestObserver.onCompleted(), marking that the server processing is completed @Override public void onCompleted() { // Callback client responseObserver.onNext responseObserver.onNext(ClientToServerStreamRpcRes.newBuilder().setPointCount(pointCount).build()); // Callback client responseObserver.onCompleted flag completed responseObserver.onCompleted(); } }; } /** * Bidirectional streaming RPC, which accepts batch RouteNote type data and returns batch RouteNote type data * @param responseObserver * @return */ @Override public StreamObserver<TwoWayStreamRpcReq> listTwoWayStreamRpcRes(StreamObserver<TwoWayStreamRpcRes> responseObserver) { // Construct observer client interaction return new StreamObserver<TwoWayStreamRpcReq>() { // Response client @Override public void onNext(TwoWayStreamRpcReq point) { // Callback client responseObserver.onNext responseObserver.onNext(TwoWayStreamRpcRes.newBuilder().setMessage("res" + point.getMessage()) .setLocation(Point.newBuilder() .setLatitude(point.getLocation().getLatitude() + 100) .setLongitude(point.getLocation().getLongitude() + 100) .build()) .build()); } @Override public void onError(Throwable t) { System.out.println("client to server stream rpc is cancelled"); } // Triggered when the client calls requestObserver.onCompleted(), marking that the server processing is completed @Override public void onCompleted() { // Callback client responseObserver.onCompleted flag completed responseObserver.onCompleted(); } }; } }
client
import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.examples.routeguide.*; import io.grpc.stub.StreamObserver; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class GRpcClient { //Remote connection manager, managing the lifecycle of connections private final ManagedChannel channel; private final RouteGuideGrpc.RouteGuideBlockingStub blockingStub; private final RouteGuideGrpc.RouteGuideStub stub; Random random = new Random(); public GRpcClient(String host, int port) { //Initialize connection channel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); //Initialize remote service Stub blockingStub = RouteGuideGrpc.newBlockingStub(channel); stub = RouteGuideGrpc.newStub(channel); } public void shutdown() throws InterruptedException { //Close connection channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } public static void main(String[] args) throws Exception { GRpcClient client = new GRpcClient("127.0.0.1", 50051); // Simple gRPC interaction System.out.println("simple rpc start!!!!!!"); SimpleRpcRes simpleRpcRes = client.getSimpleRpcRes(12, 16); System.out.println("simple rpc response: "); System.out.println(simpleRpcRes); System.out.println("simple rpc over!!!!!!"); System.out.println("\n===================================================\n"); // Server to client streaming interaction System.out.println("server to client stream rpc start!!!!!!"); Iterator<ServerToClientStreamRpcRes> serverToClientStreamRpcResIterator = client.listServerToClientStreamRpcRes(11, 22, 666, 777); System.out.println("server to client stream rpc response: "); while (serverToClientStreamRpcResIterator.hasNext()) { System.out.println(serverToClientStreamRpcResIterator.next()); } System.out.println("server to client stream rpc over!!!!!!"); System.out.println("\n===================================================\n"); // Client to server streaming interaction System.out.println("client to server stream rpc start!!!!!!"); client.getClientToServerStreamRpcRes(10); System.out.println("client to server stream rpc over!!!!!!"); System.out.println("\n===================================================\n"); // Client to server streaming interaction System.out.println("two way stream rpc start!!!!!!"); client.listTwoWayStreamRpcRes(5); System.out.println("two way stream rpc over!!!!!!"); //Close connection client.shutdown(); } public SimpleRpcRes getSimpleRpcRes(int lat, int lon) { // Construct service call parameter object SimpleRpcReq request = SimpleRpcReq.newBuilder().setLatitude(lat).setLongitude(lon).build(); // Call remote service method return blockingStub.getSimpleRpcRes(request); } public Iterator<ServerToClientStreamRpcRes> listServerToClientStreamRpcRes(int lowLag, int lowLon, int highLag, int highLong) { // Construct service call parameter object ServerToClientStreamRpcReq request = ServerToClientStreamRpcReq.newBuilder() .setLo(Point.newBuilder().setLatitude(lowLag).setLongitude(lowLon).build()) .setHi(Point.newBuilder().setLatitude(highLag).setLongitude(highLong).build()).build(); // Call remote service method return blockingStub.listServerToClientStreamRpcRes(request); } private void getClientToServerStreamRpcRes(int pointNum) throws Exception { List<ClientToServerStreamRpcReq> pointList = new ArrayList<>(); for (int i = 1; i <= pointNum; i++) { pointList.add(ClientToServerStreamRpcReq.newBuilder().setLatitude(i).setLongitude(i).build()); } CountDownLatch finishLatch = new CountDownLatch(1); // Create responseObserver to call back the client from the server StreamObserver<ClientToServerStreamRpcRes> responseObserver = new StreamObserver<ClientToServerStreamRpcRes>() { // Response server responseObserver.onNext callback @Override public void onNext(ClientToServerStreamRpcRes res) { System.out.println("client to server stream rpc response: "); System.out.println(res); } // Response server responseObserver.onError callback @Override public void onError(Throwable t) { System.out.println("client to server stream is error " + t.getMessage() + ": " + t); } // Callback of responseObserver.onCompleted on the response server @Override public void onCompleted() { System.out.println("client to server stream, server has been over"); finishLatch.countDown(); } }; // The call is initiated through asynchronous stub, and the parameter is responseObserver StreamObserver<ClientToServerStreamRpcReq> requestObserver = stub.getClientToServerStreamRpcRes(responseObserver); try { for (ClientToServerStreamRpcReq point : pointList) { // Thread.sleep(random.nextInt(1000) + 500); if (finishLatch.getCount() == 0) { return; } // Call requestObserver.onNext several times to write data to the server requestObserver.onNext(point); } } catch (Exception e) { requestObserver.onError(e); } // Mark end of client write requestObserver.onCompleted(); // Because the result is obtained asynchronously, sleep for one second, otherwise the program will be over Thread.sleep(1000); } private void listTwoWayStreamRpcRes(int pointNum) throws Exception { List<TwoWayStreamRpcReq> rpcReqList = new ArrayList<>(); for (int i = 1; i <= pointNum; i++) { rpcReqList.add(TwoWayStreamRpcReq.newBuilder().setMessage("" + i) .setLocation(Point.newBuilder().setLatitude(i).setLongitude(i).build()) .build()); } CountDownLatch finishLatch = new CountDownLatch(1); // Create responseObserver to call back the client from the server StreamObserver<TwoWayStreamRpcRes> responseObserver = new StreamObserver<TwoWayStreamRpcRes>() { // Response server responseObserver.onNext callback @Override public void onNext(TwoWayStreamRpcRes res) { System.out.println("two way stream rpc response: "); System.out.println(res); } // Response server responseObserver.onError callback @Override public void onError(Throwable t) { System.out.println("two way stream is error " + t.getMessage() + ": " + t); } // Callback of responseObserver.onCompleted on the response server @Override public void onCompleted() { System.out.println("two way stream, server has been over"); finishLatch.countDown(); } }; // The call is initiated through asynchronous stub, and the parameter is responseObserver StreamObserver<TwoWayStreamRpcReq> requestObserver = stub.listTwoWayStreamRpcRes(responseObserver); try { for (TwoWayStreamRpcReq point : rpcReqList) { // Thread.sleep(random.nextInt(1000) + 500); if (finishLatch.getCount() == 0) { return; } // Call requestObserver.onNext several times to write data to the server requestObserver.onNext(point); } } catch (Exception e) { requestObserver.onError(e); } // Mark end of client write requestObserver.onCompleted(); // Because the result is obtained asynchronously, sleep for one second, otherwise the program will be over Thread.sleep(1000); } }