9. Zhang Long netty Learns Grpc

Posted by kyllerj on Sun, 15 Sep 2019 11:16:05 +0200

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();
    }
}

Topics: Java Netty Gradle Google