Rust and gRPC: A complete guide - LogRocket Blog (2024)

gRPC is an open-source remote procedure call system developed by Google. gRPC allows the system to communicate in and out of data centers, efficiently transferring data from mobile, IoT devices, and backends to one and other.

gRPC comes with pluggable support for load balancing, authentication, tracing, etc., supports bidirectional streaming over HTTP/2, and provides an idiomatic implementation in 10 languages.

Furthermore, gRPC can generate efficient client libraries and uses the protocol buffer format to transfer data over the wire. Protocol buffers are a binary format for data transmission. Since they are binary, protocol buffers can be serialized fast. The structure of each message must be predefined.

gRPC support in Rust

The Rust community has developed many gRPC implementations, notably the tonic and grpc crates. Both provide a full implementation of gRPC protocols.

What is tonic?

tonic is a fast production-ready gRPC library with async/await support out of the box. It focuses on flexibility and reliability. tonic has full implementation of gRPC protocols over HTTP/2. tonic has built-in support for compiling protocol buffer to Rustlang. It also supports unidirectional as well as bidirectional streaming.

What is grpc?

grpc is not production-ready but is worth keeping an eye on. The crate has a working gRPC protocol implementation and supports TLS.

To show tonic and grpc in action, let’s walk through creating a demo gRPC app.

Building a gRPC app using tonic

We’ll start by creating a Rust project using cargo new grpc-demo-tonic. Create src/server.rs and src/client.rs to hold code for the gRPC server and client, respectively. We’ll add a build.rs to compile protocol buffers.

Some basic boilerplate, such as build.rs, is necessary to compile protocol buffer to Rust code. Some changes to Cargo.toml and file structure are also required.

Cargo.toml:

[package]name = "grpc-demo-tonic"version = "0.1.0"authors = ["anshul <[emailprotected]>"]edition = "2018"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]# server binary[[bin]] name = "server" path = "src/server.rs"# client binary[[bin]] name = "client" path = "src/client.rs"

File structure

├── build.rs├── Cargo.lock├── Cargo.toml├── src ├── client.rs └── server.rs

Start by creating a protocol buffer file.

 // version of protocol buffer used syntax = "proto3"; // package name for the buffer will be used later package hello; // service which can be executed service Say { // function which can be called rpc Send (SayRequest) returns (SayResponse); } // argument message SayRequest { // data type and position of data string name = 1; } // return value message SayResponse { // data type and position of data string message = 1; }We can include generated rust code in-app using `tonic`. Let’s create an `hello.rs` file to reuse in both server and client. // this would include code generated for package hello from .proto file tonic::include_proto!("hello");

Creating a gRPC server with tonic

Create a service by implementing the Say trait for a struct. The service may include multiple RPCs. Since Rust doesn’t support async traits, we have to use an asyc_trait macro to overcome this limitation.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

Add the following code to server.rs.

 use tonic::{transport::Server, Request, Response, Status}; use hello::say_server::{Say, SayServer}; use hello::{SayResponse, SayRequest}; mod hello; // defining a struct for our service #[derive(Default)] pub struct MySay {} // implementing rpc for service defined in .proto #[tonic::async_trait] impl Say for MySay { // our rpc impelemented as function async fn send(&self,request:Request<SayRequest>)->Result<Response<SayResponse>,Status>{ // returning a response as SayResponse message as defined in .proto Ok(Response::new(SayResponse{ // reading data from request which is awrapper around our SayRequest message defined in .proto message:format!("hello {}",request.get_ref().name), })) } } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // defining address for our service let addr = "[::1]:50051".parse().unwrap(); // creating a service let say = MySay::default(); println!("Server listening on {}", addr); // adding our service to our server. Server::builder() .add_service(SayServer::new(say)) .serve(addr) .await?; Ok(()) }

The above example uses tokio for async runtime and executor. The MySay struct implements the service Say. The Server type provided by tonic takes services and creates an HTTP server supporting the gRPC protocol on the given address.

Creating a gRPC client with tonic

Since gRPC defines request and response in a machine-readable format, we don’t need to implement client-side code. The code generated by the protocol buffer compiler already includes client code you can use by directly importing it.

use hello::say_client::SayClient;use hello::SayRequest;mod hello;#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {// creating a channel ie connection to server let channel = tonic::transport::Channel::from_static("http://[::1]:50051") .connect() .await?;// creating gRPC client from channel let mut client = SayClient::new(channel);// creating a new Request let request = tonic::Request::new( SayRequest { name:String::from("anshul") }, );// sending request and waiting for response let response = client.send(request).await?.into_inner(); println!("RESPONSE={:?}", response); Ok(())}

Rust and gRPC: A complete guide - LogRocket Blog (3)

To test our small application, run cargo run --bin server and then cargo run --bin client.

Building a gRPC app using grpc

First, create a Rust project using cargo new grpc-demo-grpc. We need to add two binaries: server and client, just like in the tonic demo. We must also add the proto/hello.proto file and build.rs

Cargo.toml:

[package]name = "grpc-demo-grpc"version = "0.1.0"authors = ["anshul <[emailprotected]>"]edition = "2018"[[bin]]name="server"path="./src/server.rs"[[bin]]name="client"path="./src/client.rs"[dependencies]protobuf = "2"httpbis = { git = "https://github.com/stepancheg/rust-http2" }grpc ="*"grpc-protobuf="*"[build-dependencies]protoc-rust-grpc = "0.8.2"

build.rs:

protoc_rust_grpc requires protoc compiler in the PATH variables. It cab be downloaded from official website.

fn main() { // compile protocol buffer using protoc protoc_rust_grpc::Codegen::new() .out_dir("src") .input("./proto/hello.proto") .rust_protobuf(true) .run() .expect("error compiling protocol buffer");}

Creating a server with grpc

The grpc crate’s APIs are very similar to tonic crate’s. Like tonic, grpc generates code for gRPC communication.

For creating service, the Say trait is implemented on a struct. The implemented struct is passed to the add_server method on the ServerBuilder struct provided by the grpc crate.

use grpc::{ServerHandlerContext,ServerRequestSingle,ServerResponseUnarySink};// importing generated gRPC codeuse hello_grpc::*;// importing types for messagesuse hello::*;mod hello;mod hello_grpc;struct MySay;impl Say for MySay { // rpc for service fn send( &self, _: ServerHandlerContext, req: ServerRequestSingle<SayRequest>, resp: ServerResponseUnarySink<SayResponse>, ) -> grpc::Result<()> { // create Response let mut r = SayResponse::new(); let name = if req.message.get_name().is_empty() { "world" } else { req.message.get_name() }; // sent the response println!("greeting request from {}", name); r.set_message(format!("Hello {}", name)); resp.finish(r) }}fn main() { let port =50051; // creating server let mut server = grpc::ServerBuilder::new_plain(); // adding port to server for http server.http.set_port(port); // adding say service to server server.add_service(SayServer::new_service_def(MySay)); // running the server let _server = server.build().expect("server"); println!( "greeter server started on port {}", port, ); // stopping the program from finishing loop { std::thread::park(); }}

Creating a client with grpc

Making an RPC call is as simple as creating a client and sending the data. You simply create a request and send it through the client, then wait for a response.

use std::env;use std::sync::Arc;// importing generated gRPC codeuse hello_grpc::*;// importing types for messagesuse hello::*;mod hello;mod hello_grpc;use grpc::ClientStub;use grpc::ClientStubExt;use futures::executor;fn main() { let name = "anshul"; let port =50051; let client_conf = Default::default();// create a client let client=SayClient::new_plain("::1", port, client_conf).unwrap();// create request let mut req = SayRequest::new(); req.set_name(name.to_string());// send the request let resp = client .send(grpc::RequestOptions::new(), req) .join_metadata_result();// wait for response println!("{:?}", executor::block_on(resp));}

Rust and gRPC: A complete guide - LogRocket Blog (4)

Conclusion

Rust has excellent support for gRPC. tonic in particular is a fast, production-ready gRPC implementation.

In this tutorial, we learned how to create a gRPC app using both the tonic and grpc crates. We explored protocol buffer and walked through how to compile it to Rust code.

tonic and grpc both support TLS-based authentication using the nativetls crate. This is just the tip of the iceberg; both tonic and grpc have almost full implementations of error handling, load balancing, and authentication.

Here’s a quick visual comparison:

Featuretonicgrpc
Production-readyYesNo
Full async/await supportYesNo
Request and response streamingYesYes
TLS-based authenticationYesYes

LogRocket: Full visibility into web frontends for Rust apps

Debugging Rust applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Rust app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Modernize how you debug your Rust apps — start monitoring for free.

Rust and gRPC: A complete guide - LogRocket Blog (2024)

FAQs

What is the weakness of gRPC? ›

Limited browser support. Since gRPC heavily relies on HTTP/2, you can't call a gRPC service from a web browser directly, because no modern browsers can access HTTP/2 frames.

Is gRPC faster than REST api? ›

Protocol: gRPC relies on HTTP/2, providing better performance and reduced latency, while REST uses HTTP/1.1. Data format: gRPC employs Protocol Buffers, a binary serialization format, resulting in smaller payloads and faster communication; REST usually leverages JSON or XML, which are text-based formats.

Will gRPC support rust? ›

Rust is not yet considered one of the core languages supported by the gRPC project, but in this walkthrough we will demonstrate how developers can get started building gRPC services in Rust today using the highly capable Tonic framework.

Is gRPC human readable? ›

Not human readable

gRPC messages are encoded with Protobuf by default. While Protobuf is efficient to send and receive, its binary format isn't human readable.

Why gRPC is not popular? ›

gRPC requires more processing power and memory than some other communication protocols, which can be a problem if you're working with limited resources.

Why is gRPC bad? ›

The second bad aspect is that if you use the gRPC tool set, you'll end up with a new set of objects that are different from the objects you've been using in your application up until now. Converting from one set of objects to another can be a pain.

Is gRPC the future? ›

This is where gRPC comes in - it offers a modern, high-performance alternative to REST that is better suited to the needs of modern software development. By using binary Protocol Buffers instead of text-based formats like JSON, gRPC can achieve faster and more efficient communication between services.

Is gRPC deprecated? ›

gRPC C-core is in maintenance mode and will be deprecated in favor of gRPC for . NET. gRPC C-core is not recommended for new apps.

Is gRPC better for Microservices? ›

gRPC is widely used for communication between internal microservices majorly due to its high performance and its polyglot nature. gRPC uses HTTP/2 as its transfer protocol and hence it inherits the benefits like binary framing from HTTP/2.

Is Netflix using gRPC? ›

At Netflix, we heavily use gRPC for the purpose of backend to backend communication. When we process a request it is often beneficial to know which fields the caller is interested in and which ones they ignore. Some response fields can be expensive to compute, some fields can require remote calls to other services.

Does Netflix use gRPC? ›

Many leading tech firms have adopted gRPC, such as Google, Netflix, Square, IBM, Cisco, & Dropbox. This framework relies on HTTP/2, protocol buffers, and other modern technology stacks to ensure maximum API security, performance, and scalability.

Why not use gRPC on web? ›

It's not possible to directly call a gRPC service from a browser. gRPC uses HTTP/2 features, and no browser provides the level of control required over web requests to support a gRPC client.

Why choose gRPC over REST? ›

gRPC, unlike REST, was designed specifically to allow developers to create high-performance APIs for microservice architectures across distributed data centers. It's better suited for internal systems that require real-time streaming and large data loads.

Does Google still use gRPC? ›

gRPC is a Cloud Native Computing Foundation (CNCF) project. Google has been using a lot of the underlying technologies and concepts in gRPC for a long time. The current implementation is being used in several of Google's cloud products and Google externally facing APIs.

What are the 4 types of gRPC? ›

Understanding the Message Flow in gRPC Communication Patterns. In the previous chapter, we discussed four communication patterns supported by gRPC. They are simple RPC, server-streaming RPC, client-streaming RPC, and bidirectional-streaming RPC.

What are the limitations of gRPC web? ›

It's not possible to directly call a gRPC service from a browser. gRPC uses HTTP/2 features, and no browser provides the level of control required over web requests to support a gRPC client.

Is gRPC slower than REST? ›

Performance and Efficiency: HTTP/1.1 VS HTTP/2

gRPC, on the other hand, utilizes HTTP/2, which is even faster for interprocess communication. HTTP/1.1 is slower compared to HTTP/2. HTTP/2 was designed to overcome the limitations of HTTP/1.1, making gRPC faster in terms of request response compared to REST.

What is gRPC streaming limitation? ›

A gRPC streaming method is limited to receiving one type of message and sending one type of message.

What is gRPC insecure? ›

Found an insecure gRPC connection. This creates a connection without encryption to a gRPC client/server. A malicious attacker could tamper with the gRPC message, which could compromise the machine.

Top Articles
Latest Posts
Article information

Author: Moshe Kshlerin

Last Updated:

Views: 5802

Rating: 4.7 / 5 (57 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Moshe Kshlerin

Birthday: 1994-01-25

Address: Suite 609 315 Lupita Unions, Ronnieburgh, MI 62697

Phone: +2424755286529

Job: District Education Designer

Hobby: Yoga, Gunsmithing, Singing, 3D printing, Nordic skating, Soapmaking, Juggling

Introduction: My name is Moshe Kshlerin, I am a gleaming, attractive, outstanding, pleasant, delightful, outstanding, famous person who loves writing and wants to share my knowledge and understanding with you.