Hello guys, in this post I will introduce you about gRPC and why we should consider this option when we are looking for the best communication between microservices.

First, let me give you an overview of gRPC and protocol buffers.

Overview

gRPC is a high-performance, lightweight communication framework designed for making traditional RPC calls that were developed by Google.

How it works

In gRPC a client can easily access and call methods on service application, but first, you need to specify the methods that can be called remotely. On the server side, the server implements this methods and runs a gRPC server to handle client calls. On the client side, the client has a stub that provides the same methods as the server. On the transportation\communication layer is use by default the protocol buffer to serialize the data and here is the performance magic that I will explain bellow.

Reference: https://grpc.io/docs/guides

Protocol buffer

Protocol buffers, is a protocol developed by Google to allow serialization and deserialization of structured data. The main reason that gRPC use protocol buffer instead of JSON is the performance and the typed data structure that provides a better maintainability and flexibility.

In service to service test, made in this article, we can see why some companies as migrating to gRPC, it is because gRPC is 78% faster than JSON.

Reference: https://auth0.com/blog/beating-json-performance-with-protobuf

Talk is cheap. Show me the code

Well, I explained about all the things that involve the gRPC, now it is time to code.

First, let us see what we will use to build a simple gRPC server and client application:

Let is start, to create a maven project, run the command above:

mvn archetype:generate -DgroupId=io.grpc -DartifactId=hello-grpc -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

In your project add this dependencies and plugins:

<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty-shaded</artifactId>
  <version>1.15.0</version>
</dependency>
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.15.0</version> </dependency>
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.15.0</version> </dependency>

<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:3.5.1-1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.15.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

Now, let us create a common domain, everybody knows that a car has a model, brand and the year that it was released, so let us model it using protocol buffer.

To do this, we need to create a new directory named proto at the src/main path and create a file named carService.proto.

We will use the protocol buffer language to structure our data. All the data structure must be written in a .proto file. This file can be used to generate data access in a variety of languages, such as Java, go and c++.

So, let is declare a service and some methods in our carService.proto file:

syntax = "proto3";
option java_multiple_files = true;
package io.grpc.domain;

message CarRequest {

string model = 1;
Brand brand = 2;
int32 year = 3;

}

message CarResponse {

int32 id = 1;
string model = 2;
Brand brand = 3;
int32 year = 4;

}

enum Brand {

BMW = 0;
AUDI = 1;
PORSCHE = 2;

}

service CarService {

rpc createCar(CarRequest) returns (CarResponse);

}

Tip: If you use Intellij, I suggest use the Protobuf Support plugin 

Now, that we declared our methods and services, we can generate a code in any language that supports gRPC, in Java to do this, we need to run the command above:

mvn compile

Server side

After generating the data, let us build the server side, to do this, create a main class that starts the gRPC server with the carService.

package io.grpc;

import java.io.IOException;

public class ServiceApplication {

public static void main(String... arguments) throws InterruptedException, IOException {
Server server = ServerBuilder
.forPort(8080)
.addService(new CarService())
.build();

System.out.println("Started application");
server.start();
server.awaitTermination();
}

}

To handle the calls we need to create a service and add it to the gRPC server,  to do this, we need to create a class that extends the base service class (CarServiceImplBase) that was generated and overrides the declared methods (createCar):

package io.grpc;

import io.grpc.domain.CarRequest;
import io.grpc.domain.CarResponse;
import io.grpc.domain.CarServiceGrpc.CarServiceImplBase;
import io.grpc.stub.StreamObserver;
import java.util.Random;

public class CarService extends CarServiceImplBase {

@Override
public void createCar(CarRequest request, StreamObserver<CarResponse> responseObserver) {
System.out.println("Received request " + request);

CarResponse carResponse = CarResponse.newBuilder()
.setId(new Random().nextInt())
.setModel(request.getModel())
.setBrand(request.getBrand())
.setYear(request.getYear())
.build();

System.out.println("Created car " + carResponse);
responseObserver.onNext(carResponse);
responseObserver.onCompleted();
}

}

Client side

Our server side is done, now let us create the client side and see some kind of requests that we can do.

So create a main class that will consume the data generated to do some remote produce calls:

package io.grpc;

import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.domain.Brand;
import io.grpc.domain.CarRequest;
import io.grpc.domain.CarResponse;
import io.grpc.domain.CarServiceGrpc;
import io.grpc.domain.CarServiceGrpc.CarServiceFutureStub;
import java.util.concurrent.ExecutionException;

public class ClientApplication {

public static void main(String... arguments) throws ExecutionException, InterruptedException {
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 8080)
.usePlaintext(true)
.build();

CarRequest carRequest = CarRequest.newBuilder()
.setModel("BMW 135i")
.setYear(2018)
.setBrand(Brand.BMW)
.build();

// Created a async stub
CarServiceFutureStub carServiceFutureStub = CarServiceGrpc.newFutureStub(channel);
ListenableFuture<CarResponse> future = carServiceFutureStub.createCar(carRequest);
CarResponse carResponse = future.get();
System.out.println("Created car " + carResponse);
}

}

In the example above, we create a asynchronous stub, but there are others kinds of request that gRPC allow:

  • Unary Blocking RPC is a synchronous request and response
  • Unary Non-Blocking RPC is an asynchronous request and response
  • Server streaming RPC
  • Client streaming RPC
  • Bidirectional streaming RPC

Obs: These kinds of request is another advantage of gRPC compared to REST \ JSON.

Conclusion

In this post, we could see what is gRPC and protocol buffer and how simple is it to start. In the next posts, I will explain how to build microservices using gRPC with spring-boot and vert.x.


Luram Archanjo

I have been working directly with software development for three years using Java language, open source frameworks and tools, with excellent experience in the execution of projects, from obtaining requirements until the implementation in production. I participated in the most varied sizes of projects, from small projects for internal uses to large projects, involving clients like Google and Edenred. Solid experience in developing microservices application using Java language with the frameworks and concepts such as: Spring (MVC, Security, Boot, Data), Openshift, Kubernetes, Docker, AWS, JMS, AMQP, ORM, API, DDD and CDI.

Leave a Reply

Your email address will not be published.