Benchmarking — REST vs. gRPC. Which one is faster “gRPC + Protocol…
By: Recep İnanç
Tags: Yazılım, Protobuf, Grpc, API, Rest

REST and JSON

REST

The first API Design model we are going to look at is Representational State Transfer (REST). REST follows the principles that HTTP and World-Wide Web are based on. It is practically just a set of rules built on top of what HTTP as a protocol allowed.

For example, REST suggests that HTTP methods (GET, POST, PUT, PATCH…) are used accordingly to interact with resources, and this brings the advantage of having a predictable design when you know the resources you are dealing with. For an API to be intuitive and to be predictable has great importance. If your API sends and receives “Person” objects, then your resource would be “person”.

A sample REST API looks like this:

POST /persons -> To create new "Person"

PUT /persons/{personId} -> To update a "Person"

PATCH /persons/{personId} -> To partially update a "Person"

DELETE /persons/{personId} -> To delete a "Person"

GET /persons -> To get all "Person"s

GET /persons/{personId} -> To get a specific Person

JSON

Although in REST, data can be transferred in many forms JSON is the most commonly used one. The main reason behind that is it is being human-readable and generally performs well. Its format is pretty simple; it mainly consists of key-value pairs.

In JSON format, a person object can be represented as:

{

name: "Recep",

age: 25,

city: "Istanbul"

}

gRPC and Protocol Buffers

Remote Procedure Call (RPC)

Before getting into gRPC, let’s first talk a little bit about Remote Procedure Call (RPC). RPC is the way a software calls a procedure (call it a function or a method) of another software as if it were it is a local procedure.

There’s this insightful comparison between REST APIs and RPC APIs in terms of their usability that says: “Learning an RPC API is like learning a library while learning a REST API is like learning a database schema” — compared to a typical programming library, there is much less detail to learn in a database.

RPC transmits data in a binary format that makes data transportation more efficient.

A sample RPC API looks like this:

POST /createPerson -> To create new "Person" + {personId}

PUT /updatePerson -> To update a "Person" + {personId}

PATCH /patchPerson -> To partially update a "Person" + {personId}

DELETE /removePerson -> To delete a "Person" + {personId}

GET /listAllPersons -> To get all "Person"s

GET /loadPerson -> To get a specific Person + {personId}

For more concrete examples of RPC APIs please check Slack’s WebAPI and Perun’s API.

gRPC

gRPC is developed by Google on top of RPC.

gRPC uses HTTP/2 under the covers as its transfer protocol and makes use of the benefits of HTTP/2.

gRPC uses Protocol Buffers (Protobuf), Google’s mature open source mechanism for serializing structured data (although it can be used with other data formats such as JSON). Since HTTP/2 relies on the transferred data being binary encoded protobuf plays a very important role for gRPC.

gRPC supports code-generation for many of the most popular programming languages. This can be done as we can easily define services using Protobuf and generate client-side and server-side code based on that definition. In microservices, we may have one service written in Java (the server) and the other one in Python (the client), with gRPC’s code generation we can define our service in Protobuf and generate the stub for both Python and Java easily and we do not have to worry about the contract and the communication between the two application, we can just make use of the generated code and access the methods on server-side as if it was a method defined in client’s codebase. This feature makes gRPC a great option to consider for microservices architecture where polyglot architectures are usually chosen.

gRPC does not expose HTTP to the API designer or the API user. gRPC made all the decisions for you to map the RPC layer on top of HTTP and gRPC-generated stubs and skeletons hide HTTP from the client and server too, so nobody has to worry how the RPC concepts are mapped to HTTP — they just have to learn gRPC (I am not quite sure if it is a good thing though).

With gRPC, you can describe your API in terms of methods or procedures. However, if a lot of methods are added over time, the result can be a complex and confusing API because developers must understand what each method does individually.

Of course, you can use binary payloads and HTTP/2 without gRPC, but that requires more work and more mastery in other technologies.

The way a client uses a gRPC API is by following these three steps:

Decide which procedure to call Calculate the parameter values to use (if any) Use a code-generated stub to make the call, passing the parameter values

gRPC comes with a modular code generator called protoc. Each supported language is implemented as a separate plugin. The code generator was part of the gRPC project from its inception.

With the help of a grpc-plugin using protoc, you create Server and Client stubs that works as your local interface to the remote procedures, and under the hood, gRPC transports request to the methods of the Client’s stub to the Server’s stub. On the server-side, the request is read and the related method is invoked, and the result is sent back again following a similar route.

Protocol Buffers (Protobuf)

Google focused on simplicity and efficiency when they were designing Protocol Buffers. Protobuf has its syntax, and compiler to generate code from the Protobuf files ( .proto ) you defined to supported languages.

Protocol Buffers offer typed variables where JSON does not, this simply helps programmers with the type of the field they are reading from the data file — in JSON sometimes we have to do extra checks to decide if the field is a number or not.

Protocol Buffers represent data in the binary format while JSON is a text-based format. This means compromising on human-readability for the sake of better encoding/decoding performance. Since the payload takes much less space in binary format this also helps with the bandwidth. You can read more on it here.

I really liked this answer from StackOverflow as an example of how does the representation of the data change:

JSON (9 Bytes)

{

"id":42

}

Protobuf (2 Bytes)

0x08 0x2a

In Proto format, a person object can be represented as:

message Person {

required string name = 1;

optional int32 age = 2;

optional string city = 3;

}