A note when I learn about gRPC
Scenario
Nowadays, many systems are distributed, meaning that they consist of multiple components (or microservices) on different machines. This raises a question: how do these components communicate with each other? For example, how can a class in system B communicate with a service in system D? At first, I considered using HTTP requests as a popular solution, but then I realized that this approach wouldn’t work without a browser. That’s when I discovered Remote Procedure Call as another solution. And here is just “a note” when I learn about gRPC.
Client Library
If we are doing client-side development, we do not need to worry about how the client library (SOAP library or HTTP client library) work, how they can establish the connection to communicate, or what the communication method is, such as unidirectional or bidirectional? All of these things are managed by the browser behind the scenes:
- Establish the connection with the server.
- Decide which protocol to use by Application Layer Protocol Negotiation.
- Encrypt data using SSL (Secure Sockets Layer) or TLS (Transport Layer Security), which you may know by the locking icon on your website or the HTTPS protocol.
- Check the HTTP version that the server supports and use the appropriate version. Hence, on the client-side, we simply just “fetch” and get a beautifully formatted response. So, what happens without a browser?
You must have a client library such as SOAP library or HTTP client library that understands the protocol you use, which means that you depend on a third-party HTTP client and there is someone out there maintaining that client for you. But what happens if your client library only supports HTTP/1.1 and your manager wants to update the system to use HTTP/2.0 to take advantage of its features? Now you can only f* off your manager or change your client library or do the functionality yourself.
gRPC
Firstly, whenever we create any new protocols, we have to develop and maintain a client library for every language that we want to support. gRPC, which is developed by Google, supports the most popular programming languages such as C#, C++, Go, Java, Python, and more.
Secondly, gRPC uses HTTP/2, which allows for lower latency connections, and we can take advantage of a single connection from client to server (which is described in HTTP/2 that can result in more efficient use of connection and more efficient use of server resources), as well as this is hidden implementation, Google just did it for you. For example, when gRPC changes to use HTTP/3, you do not need to worry about your application, it just works properly.
HTTP/2 also supports bidirectional connectivity and asynchronous connectivity, then it is possible for the server to stream messages with the client (async response/noti, etc.).
Thirdly, gRPC uses a language-agnostic message format which is protocol buffers. So, whatever the language of your server is and your client is, they can communicate through protocol buffers where you compile the proto file into a binary format, and when you receive the binary on the client, you will deserialize it into your client language.
Language Agnostic
We may be familiar with the payload format as JSON in REST API, but in gRPC, it is protocol buffers (Protobufs).
Protobuf is a language-agnostic data format used to serialize structured data. Here are the basic steps to work with it:
- Define data in a proto file
- Compile into whatever language we use
- Use the compiled file to generate serialized Protobuf object
So why do we need to use Protobufs over JSON?
Protobufs are made to be more than a message format, it is also a set of rules and tools to define and exchange the messages.
Protobufs are faster than JSON, as they are in binary format which is more compact. (There are a lot of performance benchmarks between JSON vs Protobufs)
gRPC Modes
Unary RPC: This is the typical synchronous request-response cycle where the client makes a request to the server and then waits to receive a response from the server.
Server streaming RPC: When the client makes a single request to the server but expects multiple responses (a stream of responses) back from the server. For example, a video streaming platform, we make a single request to a page but get a stream of response back. The client listens to an event (usually by onDataReceived method) and whenever the server sends data the event will be triggered then the client gets the data, this works exactly like WebSockets.
Client Streaming RPC: This is the opposite of Server Streaming, essentially the client sends a stream of data to the server. This might be used if we send a huge file, and we do not want to send it all in one request so we partition them into multiple chunks. Bidirectional Streaming RPC: This is basically where both client and server can send streams of data to each other. The two streams operate independently, so clients and servers can read and write in whatever order they want. For example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. This is essentially WebSockets, which are most notably used in online games, chat apps, and more.
Workflow of gRPC
- Define the service contract of communication — The services can be defined by basic parameters and return type
- Generation of the gRPC code from the
.proto
file. Special compilers generate the operative code from the stored.proto
files with the appropriate classes for the desired target language - Implementation of the server in the chosen language
- Creation of the client stub that calls the server — The requests are then handled by the server and clients Below are some features of HTTP/2 that will support the architecture of gRPC to be faster and more flexible. As I said the properties of gRPC above and you can see a huge advantage we can take: Binary framing layer. HTTP/2 communication is divided into smaller messages and framed in binary format. Unlike text-based HTTP/1.1, it makes sending and receiving messages compact and efficient.
Multiple parallel requests. While HTTP/1.1 allows for processing just one request at a time, HTTP/2 supports multiple calls via the same channel. More than that, communication is bidirectional — a single connection can send both requests and responses at the same time.
Streaming. Real-time communication is not only possible with HTTP/2, but also has high-performance thanks to binary framing — each stream is divided into frames that can be prioritized and run via a single TCP connection, thus reducing the network utilization and processing load.
After implementing both the gRPC server and client, I have compiled a list of the pros and cons:
Pros
- Fast and compact since it uses binary format instead of JSON, which is heavier than binary data. Additionally, HTTP/2 compresses the binary payload even more so it is really smaller than JSON a lot and we can reduce the RTT even more in TCP.
- One client library maintained by Google and open source community! Do not need to depend on a third-party client or maintain by ourselves, even REST we also have a lot of HTTP client libraries and that can lead to problems as I described at first.
- Cancel request, which is not possible in a stateless environment. Every connection in HTTP/2 has a streamID and gRPC can simply use that to tell the server to cancel the connection, as well as listen to the cancelling event which is impossible in HTTP/1.1.
- Benefits from HTTP/2, all the streaming modes such as client streaming, server streaming, bidirectional, compact and compression in HTTP/2, and combine with Protobuf is just powerful.
Cons
- Still very young and not much support. When I got bugs in using gRPC, there is not a lot of information about bugs on Google or Stackoverflow, I have to follow the docs and debug myself.
- Browser support. Due to gRPC heavily depending on HTTP/2.0, it is impossible to directly call the gRPC service from the browser. To take this, you have a proxy layer to perform conversion between HTTP/1.1 and HTTP2.
- Hard to learn. gRPC is very different from REST APIs so it takes time to get used to. I find it challenging to get familiar with Protobuf, so just use REST as long as possible.
That's all I have self-studied about gRPC in my free time. Thank you for reading. If there are any inaccuracies or gaps in my knowledge, please feel free to correct me. I genuinely appreciate any input.
All rights reserved