Background & Introduction
RPC vs JSON vs SOAP
Once I learn about gRPC and Thrift, it is hard to go back to using the more transitional JSON-based REST API or SOAP API.
The two well-known RPC frameworks, gRPC, and Thrift, have many similarities. The former originates from Google while the latter originates from Facebook. Both of them are easy to use, have great support for a wide range of programming languages, and both are performant.
The most valuable feature is code generation in multiple languages and also server-side reflection. These make the API essentially type-safe. With server-side reflection, it makes it much easier to explore the schema definition of the API without having to read and understand the implementation.
Grpc vs Thrift
Apache Thrift historically has been a popular choice. However, in recent years, due to a lack of continuous support from Facebook, and fragmentation with the fork of fbthrift, it slowly lost popularity.
In the meantime, gRPC has caught up with more and more features with a healthier ecosystem.
Comparison between GRPC (blue) with Apache Thrift (red). Google Trends
GitHub star history between gRPC, fbThrift, and Apache Thrift. https://star-history.com
As of today, unless your application is affiliated with Facebook in someway, there is no good reason to consider Thrift.
How about GraphQL?
GraphQL is another framework initiated from Facebook. It shares many similarities with the two RPC frameworks above.
One of the biggest pain points in mobile API development is that some users never upgrade their app. Because we want to maintain backward compatibility, we either have to keep old unused fields in the API or create multiple versions of the API. One motivation of GraphQL was to solve that problem. It is designed to be a “query language” and allow the client to specify what data fields that it needs. This makes it easier to handle backward compatibility.
GraphQL has great value in developing mobile APIs as well as public facing APIs (such as GitHub). Since, in both cases, we could not easily control the client behavior.
However, if we are building an API for the web frontend or API for internal backend services, there is little benefit of choosing GraphQL over gRPC.
Rust
Above is a small overview of the networking frameworks so far. Besides networking, we also need to decide on a language for the application server.
Based on Stack Overflow Survey: “For the sixth-year, Rust is the most loved language.” Its type-safety, elegant memory management, and wide community support, and performance, all make Rust a very attractive and promising programming language for backend service developments despite the relatively steeper learning curve.
Rust is the most loved language. Stack Overflow Survey 2021
We also start seeing wider and wider adoption of Rust in the industry: Facebook, Dropbox, Yelp, AWS, Google, etc. It is clear that Rust will continue to grow and is here to stay.
That’s what we’ll look in in today’s tutorial — building a small server with gRPC in Rust.
Install Rust
Install Rust with the following:
$ curl --proto '=https' --tlsv1.2 -sSf <https://sh.rustup.rs> | sh
If you’ve previously installed Rust, we can update it via:
$ rustup update stable
Let’s double-check the installed version of rustc
(the Rust compiler) and cargo
(the Rust package manager):
$ rustc --version
rustc 1.60.0 (7737e0b5c 2022-04-04)
$ cargo --version
cargo 1.60.0 (d1fd9fe2c 2022-03-01)
For more information about installation, checkout https://www.rust-lang.org/tools/install.
Create a Rust Project
Run the following to create a new “Hello World” project:
$ cargo new rust_grpc_demo --bin
Let’s compile and run the program:
$ cd rust_grpc_demo
$ cargo run
Compiling rust_grpc_demo v0.1.0 (/Users/yuchen/Documents/rust_grpc_demo)
Finished dev [unoptimized + debuginfo] target(s) in 1.75s
Running `target/debug/rust_grpc_demo`
Hello, world!
This shows the file structures we have so far:
$ find . -not -path "./target*" -not -path "./.git*" | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/| - \1/"
|-Cargo.toml
|-Cargo.lock
|-src
| |-main.rs
Define gRPC Interface
gRPC uses Protocol Buffers for serializing and deserializing data. Let’s define the server API in a .proto
file.
$ mkdir proto
$ touch proto/bookstore.proto
We define a book store service, with only one method: provide a book id, and return some details about the book.
We will create our gRPC service with tonic. Add the following dependencies to the Cargo.toml
file:
To generate Rust code from bookstore.proto
, we use tonic-build
in the crate’s build.rs
build-script.
$ touch build.rs
Add the following to the build.rs
file:
One thing specific to point out that we added this .out_dir(“./src”)
to change the default output directory to the src
directory so that we can see the generated file easier for the purpose of this post.
One more thing before we are ready to compile.tonic-build
depends on the Protocol Buffers compiler to parse .proto
files into a representation that can be transformed into Rust. Let’s install protobuf
:
$ brew install protobuf
And double check that the protobuf compiler is installed properly:
$ protoc --version
libprotoc 3.19.4
Ready to compile:
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
With this, we should have a file src/bookstore.rs
generated. At this point, our file structure should look like this:
| - Cargo.toml
| - proto
| | - bookstore.proto
| - Cargo.lock
| - build.rs
| - src
| | - bookstore.rs
| | - main.rs
Implement the Server
Finally, time to put the service together. Replace the main.rs
with the following:
As we can see, for the sake of simplicity, we don’t really have a database of book setup. In this endpoint, we simply return a fake book.
Time to run the server:
$ cargo run
Compiling rust_grpc_demo v0.1.0 (/Users/yuchen/Documents/rust_grpc_demo)
Finished dev [unoptimized + debuginfo] target(s) in 2.71s
Running `target/debug/rust_grpc_demo`
Bookstore server listening on [::1]:50051
Nice we have our gRPC server in Rust up and running!
Bonus: Server Reflection
As stated at the beginning, I was first impressed by gRPC initially because of its capability of doing server reflection. Not only is it handy during service development, but it also makes communication with frontend engineers a lot easier. So, it wouldn’t be complete to conclude this tutorial without explaining how to add that for the Rust Server.
Add the following to the dependencies:
tonic-reflection = "0.4.0"
Update build.rs
. The lines that need to be changed are marked with // Add this
comment.
And finally, update the main.rs
to the following.
Testing The gRPC Server
There are many GUI clients to play with gRPC Server, such as Postman, Kreya, bloomrpc, grpcox, etc. To keep things simple for today, we will use a command line tool grpc_cli
.
To install:
$ brew install grpc
And to test out our first gRPC endpoint:
$ grpc_cli call localhost:50051 bookstore.Bookstore.GetBook "id: 'test-book-id'"
connecting to localhost:50051
Received initial metadata from server:
date : Sun, 08 May 2022 20:15:39 GMT
id: "test-book-id"
name: "Zero to One"
author: "Peter"
year: 2014
Rpc succeeded with OK status
Looks like it works! And that, my friend, is how we build a gRPC server in Rust.
That’s it for today. Thanks for reading and happy coding! As usual, the source code is available on GitHub.
This article is also available at https://betterprogramming.pub/be2c52f0860e.