gRPC implementation in Springboot and microservices

There are many ways of communication between services. We can make use of REST/SOAP web services, RPCs or the more advanced technique gRPC with less latency.

gRPC provides better features compare to other ways of message exchange. So, for communication between microservices we can use gRPC for example.

Features of gRPC:

  1. The gRPC uses HTTP/2 protocol as transport for communication which is faster than REST
  2. Message exchanged in the form of Protocol Buffers (Google’s mature open source mechanism for serializing structured data)
  3. We can generate the code using .proto files
  4. Allows bi-directional streaming
  5. Requires gRPC -web to invoke

gRPC Architecture:

The architecture of gRPC is as below. It can be seen that server and client can be written in different languages and gRPC will take care of the conversions required.

gRPC Server implementation:

gRPC server is the application that exposes gRPC services to be consumed by gRPC client.

We will be using gradle as build tool here and need below plugins, dependencies and protobuf settings in build.gradle file:

plugins {
 id 'java'
 id 'org.springframework.boot' version '2.7.5'
 id 'io.spring.dependency-management' version '1.0.15.RELEASE'
 id 'com.google.protobuf' version '0.8.14'
}

dependencies {
 implementation 'org.springframework.boot:spring-boot-starter'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 implementation group: 'io.grpc', name: 'grpc-netty', version: '1.50.2'
 implementation group: 'io.grpc', name: 'grpc-protobuf', version: '1.50.1'
 implementation group: 'io.grpc', name: 'grpc-stub', version: '1.50.1'
 implementation group: 'net.devh', name: 'grpc-server-spring-boot-starter', version: '2.13.1.RELEASE'
}

protobuf {
 generatedFilesBaseDir = "$projectDir/src/main/java/generated"
 protoc {
  artifact = 'com.google.protobuf:protoc:3.10.1'
 }

 plugins {
  grpc {
   artifact = 'io.grpc:protoc-gen-grpc-java:1.25.0'
  }
 }

 generateProtoTasks {
  all()*.plugins {
   grpc {}
  }
 }
}

Add .proto file in main/proto folder with below contents.

HelloService.proto =>

syntax = "proto3";
option java_multiple_files = true;
package com.example.grpcserver;


service HelloService {
  rpc hello(HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string firstName = 1;
  string lastName = 2;
}

message HelloResponse {
  string greeting = 1;
}
  1. proto3: The first line of the file specifies that you’re using proto3 syntax. This must be the first non-empty, non-comment line of the file. If not provided, protocol buffer compiler will assume it as proto2
  2. java_multiple_files:
  • If set to false, only a single .java file will be generated for this .proto file, and all the Java classes/enums/etc. generated for the top-level messages, services, and enumerations will be nested inside of an outer class.
  • If set to true, separate .java files will be generated for each of the Java classes/enums/etc. generated for the top-level messages, services, and enumerations, and the wrapper Java class generated for this.

3. package: where the generated code need to be stored.

4. service: provide the required service and method names( in our case, we have given hello(), taking HelloRequest as parameter and returns HelloResponse as return type whose contents also the part of .proto file).

More details on the syntax of .proto files can be found here

Run gradle clean build to generate the files. The below files will be generated in the configured path.

The set of files will be generated. We need to extend the class HelloServiceGrpc.HelloServiceImplBase in generated folder and provide implementation for hello() method. Annotate the class with @GrpcService to mark a gRPC service implementation for automatic inclusion in your server.

package com.example.grpcserver.service;


import com.example.grpcserver.HelloRequest;
import com.example.grpcserver.HelloResponse;
import com.example.grpcserver.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {

    @Override
    public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
        System.out.println("inside overriden method hello()");
        HelloResponse response = HelloResponse.newBuilder().setGreeting("Hello "+ request.getFirstName()+" "+request.getLastName()+". Welcome").build();
        System.out.println("returning :"+response);
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Test the service using BloomRPC:

One of the ways to test gRPC service method is using BloomRPC application. We need to add the .proto file in server to bloomRPC and provide required parameters. And hit the play button. If everything is fine, you will get the response in right side as below:

By default the gRPC server port is 9090. If any other port need to be used (e.g.: 8080 ), add below in application.properties file:

grpc.server.port=8080

gRPC client implementation(another microservice):

We need below plugins, dependencies and protobuf settings in build.gradle file:

plugins {
 id 'java'
 id 'org.springframework.boot' version '2.7.5'
 id 'io.spring.dependency-management' version '1.0.15.RELEASE'
 id 'com.google.protobuf' version '0.8.14'
}


dependencies {
 implementation 'org.springframework.boot:spring-boot-starter'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 implementation group: 'io.grpc', name: 'grpc-netty', version: '1.50.2'
 implementation group: 'io.grpc', name: 'grpc-protobuf', version: '1.50.1'
 implementation group: 'io.grpc', name: 'grpc-stub', version: '1.50.1'
 implementation group: 'net.devh', name: 'grpc-client-spring-boot-starter', version: '2.13.1.RELEASE'
}

protobuf {
 generatedFilesBaseDir = "$projectDir/src/main/java/generated"
 protoc {
  artifact = 'com.google.protobuf:protoc:3.10.1'
 }

 plugins {
  grpc {
   artifact = 'io.grpc:protoc-gen-grpc-java:1.25.0'
  }
 }

 generateProtoTasks {
  all()*.plugins {
   grpc {}
  }
 }
}

Add the same .proto file in main/proto folder.

In application.properties file, add gRPC server address and negotiation type:

grpc.client.hello-service.address=static://localhost:9090
grpc.client.hello-service.negotiation-type=plaintext

NegotiationType identifies the negotiation used for starting up HTTP/2. More details on can be found here.

Call the gRPC service as below:

package com.example.grpcclient.client;

import org.springframework.stereotype.Service;
import net.devh.boot.grpc.client.inject.GrpcClient;

@Service
public class GrpcClientClass {

    @GrpcClient("hello-service")
    private com.example.grpcserver.HelloServiceGrpc.HelloServiceBlockingStub helloServiceBlockingStub;

    public void testingGRPC() {
        com.example.grpcserver.HelloRequest helloRequest = com.example.grpcserver.HelloRequest.newBuilder().setFirstName("Mike").setLastName("John").build();
        com.example.grpcserver.HelloResponse response = helloServiceBlockingStub.hello(helloRequest);
        System.out.println("response:"+response);
    }
}

In above code, we are invoking the “hello-service”(note: the name is given in application.properties file in client side) service from gRPC server using @GrpcClient annotation. We are forming the HelloRequest object to invoke the hello() method in gRPC server.

The complete code for gRPC server can be found on Github here and for gRPC client, here.

References:

Thank you for reading. Happy exploring!!!

Published by Ankitha Gowda

Software Developer

Leave a comment

Design a site like this with WordPress.com
Get started