Intro
If you have built RESTful or other OpenAPI-like APIs for some time and wondering what’s next for you, then you have come to the right place. This article series discusses leveraging gRPC to build your next API, even multiple services. We will initially look at the main concepts from a high-level view and then move on to the implementation aspects of it.
Motivation
There are many tutorials on getting started there. But the main issue I faced was that they even made me more confused as there was a lot of contexts lost in the process and brought in way too many third-party libraries or explained a bunch of steps without emphasizing how different pieces work together. Therefore, I thought to create a guide for anyone who’s interested in getting started with gRPC from a hands-on perspective.
This article is the first part of a series on gRPC. The links are down below. If you want to jump ahead, please feel free to do so.
- Introduction to gRPC (You are here)
- Building a gRPC server with Go
- Building a gRPC client with .NET
- Building a gRPC client with Go
- Building a gRPC client with .NET
Background
One of the popular choices for building APIs nowadays is creating a RESTful service. However, before even coming to REST API, we need to look back to see other forms we used to have in the past.
- SOAP - Popularized back in the late-90s for building service-oriented architectures (SOA) systems which are known for exchanging bloated XMLs. The benefits were detrimental for distributed applications where the schema was rigid.
-
REST - Promoted the resource-oriented architecture (ROA)-style distributed applications. Often bulky with JSONs, and everything that the service provides is represented as resources (Eg:
/api/v1/users/
orapi/v1/books/1234
etc.). Sometimes this could result in exposing too much or too little data at the cost of making multiple HTTP calls. - GraphQL - GraphQL takes a step further and exposes a single endpoint that you can use to query or mutate the data through HTTP verbs. It’s still a request-response model and based on the text-based transport protocol, HTTP 1.x.
What if I want to have some bi-directional communication? None of the above solved that. Then we got technologies like WebSockets and Server Sent Eventing.
- WebSockets - Built to support bi-directional communication over a single TCP connection. Known to be a very chatty protocol often sending packets back and forth. If you want to know about WebSockets, here is an article that I wrote.
- Server Sent Eventing - Another paradigm where the server sends messages to the client once the initial connection has been set up by the client.
There’s a recurring theme going on in the above technologies. It could be summarized as the messages being (bulky, not strongly typed etc.) and inefficient protocols (text-based such as HTTP 1.x etc.).
Hello gRPC
gRPC was born to address some of the challenges we face in the above approaches. In 2015 Google released gRPC to the open-source world. The idea behind gRPC is to enable developers to use RPC-like communications over HTTP/2 while have a single client library. Whoever is maintaining gRPC will maintain the client libraries. Because HTTP/2 works on binary format, gRPC will also abstract away the HTTP/2 stuff from you. The developers only have to define their service contracts through requests, responses and RPC calls, and the gRPC framework will handle the rest for us.
💡 Even before we start learning about gRPC, you must have also thought about what the “g” in gRPC mean? Some say it stands for “Good”; others say it stands for “Google”. You know what? It doesn’t matter as it doesn’t provide more context. You can find all the different variations here, which is, by the way, hilarious! 😂
Protocol Buffers
They are a language-agnostic way to define what your service does. These are commonly known as “IDLs” or Interface Definition Language(s).
So the steps are,
- You write the messages. These messages have statically typed fields.
- You write your services by defining what comes in, what goes out.
- Compile the proto files and generate the client libraries for your application.
It’s also worth mentioning that Protocol Buffers are not the only way to define our IDLs. There are other formats like FlatBuffers, Bond etc.
Let’s say we want to model a book shop which can be queried to get a list of books available. This is what a protocol buffer would like:
syntax = "proto3";
message Book {
string title = 1;
string author = 2;
int32 page_count = 3;
optional string language = 4;
}
message GetBookListRequest {}
message GetBookListResponse { repeated Book books = 1; }
service Inventory {
rpc GetBookList(GetBookListRequest) returns (GetBookListResponse) {}
}
The above is an example that we will use throughout this blog series. Don’t worry if you don’t understand what’s going on here. We will go in-depth in upcoming posts.
- The first line specifies the Protobuf version we will be using. It will be set to
proto2
if you don’t specify explicitly. - The Book is a
message
definition with some statically typed fields such as title, author etc. -
GetBookListRequest
andGetBookListResponse
are also messages composed of the Book type we defined above. - Inventory is a
service
that says what methods we expose to the remotely invoked clients.
There are many advantages of using Protobufs compared to something like JSON. Once you write the definitions for your service, you can share them with other teams and use them to generate stubs/code that can interact with your service.
Another advantage is that Protobufs are binary encoded. The payload is smaller than JSON, which means it would be efficient to send. This also means that it will use fewer CPU cycles to serialize/deserialize the messages.
gRPC Server & Client
Now that we have the Protobuf definitions for our service, we could generate the Server-side and Client-side implementations using the Protoc compiler.
- We first create the definition of the service with a
.proto
file - We then generate the server-side code in our preferred language (Go, C#, Java etc.). This code includes the boilerplate code to serialize, deserialize, functions for receiving and responding to messages.
- We then generate the client-side code in our preferred language (doesn’t have to be the same language we chose for the server). This includes methods that we can invoke on our server with additional code to serialize, deserialize messages.
- Depending on which gRPC mode we choose, the client-server communication happens over an HTTP/2 connection. We will discuss more on these modes in the next section.
gRPC Modes
There are 4 modes of gRPC communication styles. Following are their brief introductions. Feel free to go more in-depth by reading through the official docs.
- Unary RPC - More like our traditional APIs where we send a request and receive a single response.
- Server Streaming RPC - Client sends a request and reads until the server stops sending messages via a stream.
- Client Streaming - Reverse of the above, the client sends messages through the stream and waits for the server to read and return a response.
- Bidirectional streaming RPC - Pretty much both (2) & (3) combined - both client and server streams messages both ways.
Pros and Cons
Any technology comes with a set of advantages and disadvantages. Whether you choose to use gRPC might depend on some of these factors.
Pros
- Efficient for inter-process communication with all the good stuff that comes with HTTP/2.
- Well defined interfaces to be able to communicate also while supporting polyglot development.
- Code-generation of client and server stubs with strong types.
Cons
- It may not be suitable for external-facing services since most web browsers’ support is limited.
- Changing the service definitions might require rework and regeneration of code.
- Could be a steeper learning curve compared to other RESTful or GQL like architectural styles.
Conclusion
In this article, we looked at gRPC from a high level. In the next article, we will look at how we can put these into action and generate a gRPC service with Go. Feel free to let me know any feedback or questions. Thanks for reading ✌️
Top comments (0)