Posted on February 18, 2021 by Rajesh Rajagopalan
Setting the context
So far, REST-based APIs have been the preferred go-to architecture for most inter-service communications, instead of HTTP1.1 using JSON payload. Especially in Microservices architecture.
While REST / JSON-based communication have several benefits and is widely supported across languages and providers, it suffers from a heavy payload size because of JSON. It is therefore not performance-efficient. It also lacks the inherent support for payloads like streaming data.
gRPC over HTTP/2 using Protobuf has emerged as a high performance alternative that provides orders of magnitude improvement, and is fast becoming the go-to standard for most inter-service communications.
In this blog, I’d like to discuss traditional architectures using REST & JSON, their pros and cons, how a combination of gRPC, Protobuf and HTTP/2 helps us build performance efficient services, and how you can operationalize them like many prominent technology companies.
Microservices and communication using REST/JSON
Companies have begun to modernize their legacy systems. Several have moved to the cloud, and have adopted Microservices architecture. Unlike legacy monoliths, Microservices, as their name suggests, comprises of small, loosely coupled and fully self-contained Microservices. Each Microservice delivers a specific piece of business functionality as determined by the well accepted DDD (Domain Driven Design).
This style of architecture presents companies with a number of business benefits including independently deployable and scalable Microservices, democratization of technology, where each team owning the Microservice can develop services in a stack that is suitable, based on its characteristics, fault isolation, scalability, data isolation and so on.
While the benefits are plenty, Microservices also come with a set of challenges. Building robust communications between numerous Microservices, being the first. While finalizing the design for data exchange, there are a few key considerations we need to be aware of:
a. How to standardize communication across Microservices developed by independent teams using different technology stack?
b. How to reduce Network congestion and Latency in communication?
c. How to reduce Chatty I/O between Microservices?
d. Handling error patterns in a consistent manner across Microservices.
e. Load balancing.
Limitations of JSON, REST, HTTP/1
REST APIs using JSON payloads have worked well in many situations and continue to do so. However, the limitations become pronounced when there is a requirement is for high-performance.
- While it is human readable and easy to understand, it is considerably heavy and slow due to JSON payloads and HTTP1.1 that has not evolved sincr 1997!
- Resource / CRUD operations based and not API centric.
- Support only client requesting servers model. No inherent support for streaming data.
- No standardized way to define API contracts. Developers need to use third party solutions such as Swagger and OpenAPI.
Can these limitations be overcome by using a combination of Protobuf, gRPC and HTTP/2?
Let’s find out!
Protobuf
JSON was a significant upgrade from the days of using XML! JSON provides for human readable, easy-to-understand and flexible schema definitions. It has mature toolsets for development, testing and troubleshooting. JSON also has wide support across a number of languages.
However, for inter process communication – especially in the Microservices architecture, this results in a number of problems. Schema definition is not enforced. JSON has a large payload as a result of the text format (even after applying compression techniques likes gzip). And there is no support for comments, metadata, documentation.
Protocol buffers (Protobufs) are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data. Google set out to improve upon XML making it smaller, faster, and simpler. However, it ended up being even better than JSON that came after! Also Protobufs have thier fair share of benefits:
- Binary format unlike JSON which is text-based. This results in a significantly smaller payload size.
- For example – a sample payload for Order data below in JSON format is 533 bytes. Whereas in the Protobuf format it is just 17 bytes!
- Schema enforced by .proto definition across services
- Fully typed
- Automatic boilerplate code generation across multiple languages (used by different services) for data exchange
- Flexible schema with backward compatibility that can evolve over time
- Support for comments and documentation and metadata
Uber engineering compared various encoding and compression options and found that Protobuf with zlib performed orders of magnitude better than JSON.
gRPC
gRPC is a modern, lightweight and high-performance communication protocol from Google. While RPC-based architectures have long existed from the days of infamous CORBA, gRPC takes a modern approach using Protobuf, HTTP/2.
gRPC, like CORBA, allows you to connect to distributed, heterogeneous applications as if it’s a local call.
Developing RPC-based services involves service definition using an Interface Definition Language (IDL). gRPC natively supports service definition using Protobuf. You define methods that can be invoked remotely, and the messages that can be exchanged – request and response.
And the gRPC framework handles the complexities associated with:
- Enforcing service contracts
- Data serialization, marshaling and unmarsharling
- Network communication
- TLS to encrypt payload
- Authentication and authorization
- Observability etc.
Protobuf and gRPC are language agnostic. This helps us build servers and clients in a heterogeneous environment, while maintaining strong type safety, overcoming runtime and interoperability issues that are commonplace in a distributed environment.
This bodes well with Microservices architectures that promote autonomous services. Each team can build using their own technology stack.
This apart, gRPC has several advantages over standard REST based communication:
- Better performance and security
- Native support for bi-directional and asynchronous communication. gRPC supports the following
- Unidirectional communication: traditional client calling server resulting in a single request and response
- Server streaming: single client request resulting in multiple responses from server
- Client streaming: multiple payloads from client in a single request
- Bi-directional streaming
- API oriented and not resource oriented
- Automatic boilerplate code generation using Protobuf in multiple languages
- End-to-end plumbing is automatically handled without requiring developer effort
Performance
I’ve used a simple Java-to-Java order gRPC service, using the order.proto file above to benchmark the performance of gRPC against a similar service written in Java and Spring.
The benchmarks are based on standard gRPC request-and-response, using Protobuf and over HTTP/2. There is no business logic, database connectivity or logging (except for the timer) implemented to keep the benchmark focussed on the topic of interest.
Similarly, I used a Java-to-Java Spring Boot API and client with the same Order object.
Overall gRPC APIs performed 11 times faster than an equivalent REST API.
There are a number of other other benchmarks done – you can pick your choice of data or run your own. I have provided a few in the references section, for your consideration.
At the end of the day, there is no denying the superior performance of gRPC + Protobuf !
Challenges, and where to use or not
While gRPC with Protobuf has a significant performance benefits, we need to be aware of the current state of technology, and its limitations.
Challenges
- Limited community support
- While there is support across prominent languages for Protobuf, its not as wide as JSON
- Longer learning curve
- Longer bootstrap time – initial setup takes time compared to REST
- Lacks human readability – leading to complexity in troubleshooting problems
- Not readily suitable for external facing applications – JSON has better support
Situations where REST API is a better fit than gRPC
- Clients want to access API through browser, clients that are JavaScript/Python based where you cannot incorporate build process to use gRPC-generated client code or clients simply using cURL commands. (While this can be enabled using an API gateway such as Google Cloud Endpoint, it is not readily available feature of gRPC API)
Having said that, I believe gRPC with Protobufs should be de-facto standard / solution for communication between heterogeneous Microservices.
Conclusion
Google has been working on Stubby an internet-scale RPC framework that can scale to tens of billions of requests per second, for 15 years before they open sourced it as gRPC (“g” means something different in each release!) in 2016. It is fairly mature, with adoption across leading technology companies looking to solve for performance. A number of programming languages have out-of-the-box support for gRPC.
- gRPC enables language agnostic service definitions.
- Language-native client stubs and server skeletons are automatically generated.
- gRPC handles all the complexities involved in data serialization, native connectivity over HTTP/2, security, AuthN & AuthZ.
- gRPC enables developers to focus on API development – as easy as making local function calls.
- gRPC with Protobuf enables high performance data exchange.
While there is a learning curve before you can start building gRPC APIs, it is an ideal solution for building high-performance communication between heterogeneous Microservices.
References
- gRPC: a true internet-scale RPC framework is now 1.0 and ready for production deployments: https://cloud.google.com/blog/products/gcp/grpc-a-true-internet-scale-rpc-framework-is-now-1-and-ready-for-production-deployments
- https://docs.microsoft.com/en-us/azure/architecture/microservices/
- Designing APIs for microservices: https://docs.microsoft.com/en-us/azure/architecture/microservices/design/api-design
- gRPC – A Modern Framework for Microservices Communication: https://www.capitalone.com/tech/software-engineering/grpc-framework-for-microservices-communication/
- The courses on Udemy by Stephane Marek provide an excellent introduction to Protobuf and gRPC. https://www.udemy.com/course/grpc-java/ and https://www.udemy.com/course/protocol-buffers/
- https://developers.google.com/protocol-buffers/
- https://cloud.google.com/blog/products/api-management/understanding-grpc-openapi-and-rest-and-when-to-use-them
- Beating JSON performance with Protobuf: https://auth0.com/blog/beating-json-performance-with-protobuf/
- How Uber Engineering Evaluated JSON Encoding and Compression Algorithms to Put the Squeeze on Trip Data: https://eng.uber.com/trip-data-squeeze-json-encoding-compression/
- https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
- https://thenewstack.io/build-real-world-microservices-with-grpc/
- Evaluating Performance of REST vs. gRPC: https://medium.com/@EmperorRXF/evaluating-performance-of-rest-vs-grpc-1b8bdf0b22da
- https://github.com/grpc-ecosystem/awesome-grpc
- https://www.cncf.io/case-studies/netflix/