gRPC for microservices communication

In the olden days, a software application was built as a large monolith, and It’s still being done. However, in recent times microservices architecture has become a popular choice for developing software applications. In a microservice architecture, the microservices often need to communicate with each other. Compared to the traditional RESTful web API, a gRPC based RPC framework can be a better alternative for microservices communication.

Do you want to know more about gRPC, the remote procedure call (RPC) framework used by companies like Netflix and Dropbox?

Let’s get started:

What is microservices architecture?

  • Microservices are modeled around business capabilities.
  • Microservices are independently deployable.
  • A microservices encapsulates the data it owns. In case, if one microservice needs to get data from another microservice then it should call API.
  • A microservice should be small in size.

Microservices inter-process communication

  • Synchronous communication (request-reply): In this pattern, a service calls an API exposed by another service and waits for the response. The API must have well-defined semantics and versioning.
  • Asynchronous communication: In this pattern, a service sends a message, without waiting for a response from another service. And, one or more services can process the message.

A most common way of implementing a request-reply style of communication is by using RESTful services, typically implemented as JSON payload over HTTP. However, RESTful services can be quite bulky, inefficient, and limiting for lots of use cases. The bulkiness of RESTful services is because of the fact that most implementation relies on JSON, a text-based encoding format. The JSON format uses lots of space compared to the binary format.

There are binary encoding formats of JSON ( MessagePack, BSON, SMILE, etc.); however, their adoption is not very widespread.

Another issue with using JSON-based API is that the support of schema is optional. The most common way for an application to advertise API is by using OpenAPI spec but this is not tightly integrated with the RESTful architecture style. It’s very common to see OpenAPI spec and implementation drifting in due course.

Why gRPC for microservice communication?

The gRPC framework is based on binary encoding format protocol buffer and implemented on top of HTTP/2. It’s strongly typed, polyglot, and provides both client and server-side streaming.

In gRPC, a client application can directly call a method on a distributed server application on a different machine as if it were a local method.

So, the question is, should you move all our APIs to use gRPC?

Probably No !!

Before we answer, let’s understand some facts about APIs. There are two types of APIs:

  • Public API: these APIs that are consumed by client applications typically browser, mobile application, or other application. For example — GitHub public APIs.
  • Private API: these APIs are not exposed to the outside world, and it’s mainly used for inter-service communication within your application.

The de facto standard for using Public API is the REST over HTTP. For private APIs, you can think of using gRPC; however, you must be aware of trade-offs associated with introducing a new technology stack vs gRPC benefits. Moreover, supporting gRPC on a browser-based application is not straightforward.

One of the possible gRPC based architectures can be — to have API Gateway/BFF in front of the microservices and make all internal communication using gRPC.

A gRPC application architecture

Advantages of gRPC

Faster compared to JSON based RESTful APIs

For example, for below payload JSON encoding takes 81 bytes and the same payload in protocol-buffers takes 33 bytes ( source — Chapter 4 — Designing Data-intensive Applications by Martin Kelpmann. If you haven’t read this book you have missed something !!)

{ 
"serName": "Martin",
"favoriteNumber": 1337,
"interests": [" daydreaming", "hacking"]
}

It’s possible to implement RESTful APIs on top of the protocol buffer, but why would you want to do that?

Another reason which makes gRPC faster is that it’s implemented on top of HTTP/2. How fast? Check this comparison of HTTP/1.1 vs HTTP/2.

Strong type and well-defined interface

service ProductService { 
rpc getProduct(GetProductRequest) returns (GetProductResponse);
}

Also, you can call this from a gRPC client as:

var productResponse = productServiceBlockingStub.getProduct(productRequest);

As you can see, calling the gRPC server is as good as calling a local method.

Polyglot

Stream support

service ProductService { 
rpc getProduct(GetProductRequest) returns (stream GetProductResponse);
}

Other features

Code sample

Implementing gRPC client and server

  • Protocol Buffers: it defines messages and services exposed by the server application. gRPC services send and receive data as Protocol Buffer (Protobuf) messages.
  • Server: the server application implements and exposes RPC services.
  • Client: the client application consumes RPC services exposed by the server.

What are protocol buffers?

Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data — think XML, 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.

Protocol buffers are nothing but interface definition language (IDL) used to define API contracts in gRPC. In gRPC, you define API contracts in .proto files.

You define gRPC APIs by declaring messages and services. For example, if the order-service (gRPC client) calls the product-service (gRPC server) to fetch information about the product by passing productId, then you can define service definition in protocol buffer as :

Let’s understand the different elements of the proto file.

Protobuf message

Field numbers, such as name = 1, are used to identify fields in the binary encoded data. This means you can't change the field number from one version to another. Furthermore, this helps in the backward and forward compatibility - clients and services can ignore field numbers that they don't know about.

Service

Code generation

For our example, we will use Protobuf Gradle Plugin to generate source code in java. The protocol buffer plugin assembles the Protobuf Compiler ( protoc) command line and use it to generate Java source files from the proto files. The generated java source files should be added to the sourceSet so that they can be compiled along with Java sources.

sourceSets { 
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}

Running command gradlew build generates source code in the directory build/generated/source/proto/main/grpc and build/generated/source/proto/main/java.

gRPC Server

In our case, the gRPC server exposes one RPC method rpc getProduct(GetProductRequest) returns (stream GetProductResponse).

Implementing service definition

To implement the business logic, you should override getProduct(..) method in the autogenerated abstract class ProductServiceGrpc.ProductServiceImplBase.

The above code fetches Product information from ProductRepository and calls onNext() on StreamObserver by passing the getProductResponse. The onCompleted() method notifies the stream about successful completion.

In case of error, you can call onError() method by passing the appropriate error code.

Registering service

Implementing gRPC client stub

You can create a gRPC channel specifying the server address and port as ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(). The channel represents a virtual connection to an endpoint to perform RPC.

You can create the client stub using the newly created channel as:

var managedChannel = ManagedChannelBuilder.forAddress(host,port).usePlaintext().build(); var productServiceBlockingStub = ProductServiceGrpc.newBlockingStub(managedChannel);

There are two types of client stubs:

  • Blocking: The BlockingStub, which waits until it receives a server response.
  • Non Blocking: The NonBlockingStub, which doesn't wait for server response, but instead registers an observer to receive the response.

Here plaintext means we are setting up an unsecured connection between client and server.

Summary

Originally published at https://techdozo.dev on August 29, 2021.

--

--

Software Architect @ Schlumberger ``` Cloud | Microservices | Programming | Kubernetes | Architecture | Machine Learning | Java | Python ```

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Pankaj Kumar

Software Architect @ Schlumberger ``` Cloud | Microservices | Programming | Kubernetes | Architecture | Machine Learning | Java | Python ```