gRPC Basics
As a continuation of the evaluation of Apache AVRO, take a look at the evaluation of the protocol buffer through gRPC.
Join the DZone community and get the full member experience.
Join For FreeAs 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."z
There are two versions :
We are going to demonstrate proto3, 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.
gRPC Over REST?
Following are some reasons 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
2) Client Streaming
3) Server Streaming
4) Bi-directional Streaming
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. The 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.
xxxxxxxxxx
<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.
xxxxxxxxxx
public class OrderConfirmationServiceImpl extends
OrderConfrimationServiceGrpc.OrderConfrimationServiceImplBase {
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.
xxxxxxxxxx
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
xxxxxxxxxx
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port)
.usePlaintext()
.build();
OrderConfrimationServiceGrpc.OrderConfrimationServiceBlockingStub stub =
OrderConfrimationServiceGrpc.newBlockingStub(channel);
OrderConfirmation orderConfirmation = stub.confrim(DummyObjectCreator.createOrder());
Client code is straightforward:
- Create the channel to connect to gRPC server.
- Use the channel to create the stub.
- 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 the streaming requirements and if you want to avoid making connections per request. In the case of a microservice architecture it has the added advantage of a reduction of payload, saving the connection cost and type-safe contract.
gRPC client also supports reactive way of handling the request, so if you are developing the reactive architecture then gRPC can support that too.
In an upcoming blog we will go through other modes of communication and the reactive way of handling the gRPC calls. Stay tuned.
You can get the entire code here.
Happy Coding.
Published at DZone with permission of Milind Deobhankar. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments