Getting Started With gRPC

As part of our protocol evaluation in the last blog, we evaluated Apache AVRO. In continuation let’s try to evaluate protocol buffer (protobuf). The easiest and simplest way to evaluate protobuf is through gRPC. Let’s try to understand what is protobuf and gRPC.

What is protobuf?
Let’s take it from official website. Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data – think JSON, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

There are two versions :
1) proto2
2) proto3
We are going to demonstrate proto3 only as this is the latest. Syntax-wise there is considerable amount of difference between proto2 and proto3 but the underlying rpc concept remains the same. For extensive information about proto3 refer language guide here.

What is gRPC?
gRPC is RPC (Remote Procedure Call) framework that can run in any environment and supports multiple programming language. It uses protocol buffers as both its Interface Definition Language (IDL) and as its underlying message interchange format. Many cool feature of gRPC is bidirectional streaming and multiplexing of requests using HTTP/2. You can get more info about gRPC here.

Why gRPC over REST ?
Following are the some reason you should choose gRPC over Rest
1) gRPC transfer binary data as payload rather then text which makes communication much faster and compact.
2) gRPC use HTTP/2 with long-lived connections so overhead of making connection per request can be avoided.
3) gRPC is type-safe so if you are expecting an integer then nobody can send it as a string.
4) gRPC is few millisecond faster then REST. So if millisecond matter for you then gRPC can be good choice.
5) gRPC supports streaming so if you are looking for distributed streaming support gRPC can be pretty useful in this case.

Different gRPC communication mechanism:
1) Unary

Traditional client server request response.

2) Client Streaming

Client send the stream and server respond in one short.
E.g- Client send array of integers and server respond its sum/multiplication.

3) Server Streaming

Client send the request and server respond in form of stream.
E.g – Client request get orders and server respond stream of orders one after another.

4) Bi-directional Streaming

Client and server communicate using stream.
E.g – Chat application

In this blog we are going to demo only unary other modes of communication will be described in upcoming blogs of gRPC so stay tune.
Our use-case (Order Service calling Order Confirmation) and payload is same we used in AVRO blog so that payload comparison becomes consistent. The first part is to create a contract between the client and server so let’s do that.
Protobuf contract:

syntax = "proto3";
option java_multiple_files = true;
package com.milind.grpc;

message Order {
    string currency = 1;
    double totalAmount = 2;
    string orderId = 3;
    string emailAddress = 4;
    string cartURL =5;
    repeated LineItems lineItems = 6;
}
message LineItems {
    string sku = 1;
    string name = 2;
    string description = 3;
    string category = 4;
    string other = 5;
    double unitPrice = 6;
    double salePrice = 7;
    double quantity = 8;
    double totalPrice = 9;
}

message OrderConfirmation {
  string orderId = 1;
  repeated ConfirmedLineItems confirmedLineItems = 2;
}

message ConfirmedLineItems {
    string sku = 1;
    double confirmQuantity = 2;
}

service OrderConfrimationService {
    rpc confrim(Order) returns (OrderConfirmation);
}

I am not going to go in depth about the datatype you can find it here. Server needs to implement OrderConfrimationService::confirm which will take Order as input and return OrderConfirmation. Once the contract is defined, we need to generate the code using the following maven plugin.

            <plugin>
                <groupId>com.github.os72</groupId>
                <artifactId>protoc-jar-maven-plugin</artifactId>
                <version>3.5.1</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <protocArtifact>com.google.protobuf:protoc:3.0.0</protocArtifact>
                            <inputDirectories>
                                <include>src/main/proto</include>
                            </inputDirectories>
                            <outputTargets>
                                <outputTarget>
                                    <type>java</type>
                                    <outputDirectory>src/main/java</outputDirectory>
                                </outputTarget>
                                <outputTarget>
                                    <type>grpc-java</type>
                                    <outputDirectory>src/main/java</outputDirectory>
                                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.1</pluginArtifact>
                                </outputTarget>
                            </outputTargets>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Execute mvn clean install command to generate the code.

Creating Server:
gRPC require its own server it is not like servlet container where you will deploy your app. Creating server is very easy almost in line with npm server. Let’s do some coding. First we will provide implementation to our gRPC service.

public class OrderConfirmationServiceImpl extends 
        OrderConfrimationServiceGrpc.OrderConfrimationServiceImplBase {

    @Override
    public void confrim(Order request,
                        StreamObserver<OrderConfirmation> responseObserver){

	...
        OrderConfirmation.Builder orderbuilder = OrderConfirmation.newBuilder()
                .setOrderId(request.getOrderId());
        ...
	responseObserver.onNext(orderbuilder.build());
        responseObserver.onCompleted();
    }

}

We need to extend generated class OrderConfrimationServiceImpl Base and provide the implementation of confirm method. If you see that, confirm method return type is void so how we will pass response to the client ?. It is through StreamObserver onNext() method and then completing the stream operation by onCompeted(). Let’s create the server to listen to particular port.

public class GrpcServer {

    public static void main(String[] args) throws IOException, InterruptedException {
        int port=args.length==0?8080:Integer.parseInt(args[0]);
        Server server = ServerBuilder.forPort(port)
                .addService(new OrderConfirmationServiceImpl()).build();
        server.start();
        server.awaitTermination();
    }
}

Creating client

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port)
                .usePlaintext()
                .build();

OrderConfrimationServiceGrpc.OrderConfrimationServiceBlockingStub stub =
		OrderConfrimationServiceGrpc.newBlockingStub(channel);

OrderConfirmation orderConfirmation = stub.confrim(DummyObjectCreator.createOrder());

Client code is straightforward:
1) Create the channel to connect to gRPC server.
2) Use the channel to create the stub.
3) Call the gRPC service via stub and get the response object directly.

Note: gRPC doesn’t support null. It will throw exception at client side when you pass null in any attribute while creating the request object. If you don’t have the data then no need to set or assign in the request object rather then setting null.

Payload comparison
Following is the comparison of the payload size with the same request data:

If you see the size of payload for Protobuf is almost similar to JSON+GZip but compared to Avro it is 33% higher, but far better than JSON.

Conclusion:
gRPC is very good for streaming requirement and if you want to avoid making connection per request. In case of microservice architecture it has added advantage of reduction of payload, saving the connection cost and type-safe contract.

gRPC client also support reactive way of handling the request, so if you are developing the reactive architecture then gRPC can support that too.

In upcoming blog we will go through other modes of communication and the reactive way of handling the gRPC calls. Stay tune.

You can get entire code here.

Happy Coding.


Leave a comment