While working on a backend project of mobile based food ordering application, We had to deal with volumes of real time data transactions, literally in millions. Basically its a mobile application backed with microservices backend, whose availability and performance is much more important considering the scale.
Our backend services are basically microservices which talks to each other on purpose. And we definitely need a high performance communication mechanism for communicate between microservices. One of the microservice is exposed public and resto other microservices are internal.
We have to tabulate the pros and cons of REST which gave us a some visibility.
Why not REST?
As REST is most preferred for microservices architecture today, I started comparing REST vs gRPC. I started with the advantages of REST (while the disadvantages to follow):
- Easy to understand.
- Web infrastructure is already built on top of http
- Tools for in inspection, modification, testing are readily available.
- Loose coupling between client and server makes changes relatively easy.
- There are lot of frameworks in most of the widely used languages to create REST Api’s.
- Http status codes are well defined and helps in identifying cause of problems
Inspite of all the above, I wondered how did gRPC secure the goal! It’s now when I started reflecting on the pain points of REST.
- While creating RESTful services, most of us follow a standard practice of writing client library and all we need to do is update client library whenever there is a change in api contracts.
- Streaming is difficult and its highly impossible in most of the languages.
- Duplex streaming is not possible.
- Hard to get multiple resources in single request.
- Need semantic versioning whenever the api contract needs to be changed.
Are these pain points are not there in gRPC? How does microservices talk to each other without a client library? Had so many questions around it and found the answer. The answer is google ‘protobuf’.
Protobuf or Proto:
Google protobuf or proto is just a mechanism for serializing structured data. Proto is smaller, simpler, faster and language neutral. You can see this similar to JSON or XML but its smaller and faster compared to it.
- Language agnostic
- Machine Readable: Protobuf can be used to exchange messages between services and not over browsers because proto’s are binary or machine readable and its not human readable.
- Provides Generators to Serialize or Deserialize : Protobuf can be easily compiled to source code (Currently it supports 8 different languages JAVA, Objective c, python, c#, ruby, go , java nano, c++) with protobuf compiler (call it as protoc) and with which serialization or deserialization is easier and there is no need of hand parsing.
- Supports types and Validations: Unlike json , we can specify field types and add validations for the same in the .proto file.
- Lesser Boilerplate code : Since it supports types and has source code generators, we don’t need to write much of boilerplate code or hand parsing the response.Supports Interfaces to RPC: If you want to use your message types with an RPC (Remote Procedure Call) system, you can define an RPC service interface in a proto file and the protocol buffer compiler will generate service interface code and stubs in your chosen language.
Here is an example proto which enhances the understanding further
The above proto has three code blocks, one service declaration and other two messages. On compilation the block under service will be created as rpc stubs and with message keyword will be models with accessors. For more understanding of google proto keywords and its usage refer this detailed article in google developers forum.
A simple Client Server Architecture with gRPC
Before we jump into gRPC let’s understand how remote procedure call works .
- A client application makes a local procedure call to the client stub containing the parameters to be passed on to the server.The server run-time library receives the request and calls the server stub procedure which unmarshalls (unpacks) the passed parameters and calls the actual procedure.
- The client stub serializes the parameters through a process called marshalling forwards the request to the local client-time library in the local computer which forwards the request to the server stub.
- The server stub sends back a response to the client-stub in the same fashion, a point at which the client resumes normal execution.
gRPC is an open source framework which works similar to the above and it is language agnostic. This means you could have a grpc server written in Java handling client calls from node.js, JAVA and Go. By default, gRPC implements Protocol Buffers to marshal/unmarshal structured data as well as define the parameters and return responses for the callable methods, which is its one of the advantage.
Creating a server and client with gRPC is very simple and following are the steps
- Create the service definition and payload structure in the Protocol Buffer (.proto) file.
- Generate the gRPC code from the .proto file by compiling it using protoc
- Implement the server in one of the supported languages.
- Create the client that invokes the service through the Stub.
- Run the server and client(s).
So summary on the advantages and disadvantages of gRPC as follows
Advantages of gRPC
- High performance along with Safety : gRPC is high performance with google protobuf and http/2 protocol which is Multiplexed, single tcp connection , transports data as binary, enables duplex streaming etc.
- Duplex streaming : Allows clients side and service streaming simultaneously.
- First Class Load Balancing: gRPC has built in library feature it can intelligently pick which backend to send traffic to.
- Selective message compression: If you are streaming mixed text and images over a single stream (or really any mixed compressible content), you can turn off compression for the images.
- Auto generated client code: With protoc we can easily generate the client code and server code.
- Heavily optimized: gRPC library is under continuous benchmarks to ensure there are no speed regressions.
- Connection Pool : We can create connection pool containing persistent connection to server through managed channels with states connected or idle.
Pain Points of gRPC
- No support for browsers hence cannot be used for external services.
- No url end points hence can’t be tested with postman or curl to check the response.
- No predefined status codes and creating custom status code may end up in conflicts.
Also, I would suggest using gRPC for the following cases
- When the microservices is only internal and when one server needs to talk to the other.
- When your internal services requires duplex streaming with high load of data.
- When you don’t feel to write client libraries.
To conclude, APIs created with gRPC had given us incredible performance improvement compared to our legacy REST API where our response time for same had reduced from 15 ms to 1.71 ms for each request, where average requests ranges from 600 rpm to 1700 rpm.
More Code examples can be seen here :
https://github.com/sankarpatw/learnings/tree/master/grpc/greetor