ashishsingh.in

Quick Start to gRPC Using Rust

If you're a developer looking to build efficient and high-performance APIs, you've likely heard of gRPC. gRPC is a modern, open-source framework developed by Google for building remote procedure call (RPC) APIs. It's designed to be efficient, fast, and language-agnostic, making it an excellent choice for building microservices and other distributed systems. In this blog, we'll take you through a quick start to using gRPC with the Rust programming language.

Prerequisites

Before we dive into gRPC with Rust, make sure you have the following prerequisites installed on your system:

  1. Rust: If you haven't already, install Rust by following the instructions on the official Rust website.

  2. Protobuf Compiler: Protocol Buffers (Protobuf) is a language-agnostic data serialization format used by gRPC. You'll need the protoc compiler to generate Rust code from your Protobuf files. You can download it from the Protobuf releases page.

Creating a gRPC Service

Let's start by creating a simple gRPC service. We'll define a service that calculates the square of a number.

Step 1: Define the Service

Create a file named calculator.proto and define your service and message types using Protobuf:

syntax = "proto3";

package calculator;

service Calculator {
  rpc Square (SquareRequest) returns (SquareResponse);
}

message SquareRequest {
  int32 number = 1;
}

message SquareResponse {
  int32 result = 1;
}

In this example, we've defined a Calculator service with a single RPC method called Square. It takes an int32 as input and returns an int32 as the result.

Step 2: Generate Rust Code

Now, we'll use the protoc compiler to generate Rust code from our Protobuf definition. Run the following command in your terminal:

protoc --rust_out=./src --grpc_out=./src --plugin=protoc-gen-grpc=which grpc_rust_plugin calculator.proto

This command generates Rust code in the src directory and includes both regular message types and gRPC service definitions.

Step 3: Create the Rust Project

Initialize a new Rust project by running:

cargo new grpc_calculator
cd grpc_calculator

Step 4: Update Cargo.toml

Open the Cargo.toml file and add the necessary dependencies:

[dependencies]
tonic = "0.5"
protobuf = "2"

Step 5: Implement the Server

Create a file named main.rs and add the following code to implement the gRPC server:

use tonic::{transport::Server, Request, Response, Status};

pub mod calculator {
    tonic::include_proto!("calculator");
}

use calculator::{calculator_server::{CalculatorServer, Calculator}, SquareRequest, SquareResponse};

#[derive(Default)]
pub struct MyCalculator;

#[tonic::async_trait]
impl Calculator for MyCalculator {
    async fn square(&self, request: Request<SquareRequest>) -> Result<Response<SquareResponse>, Status> {
        let number = request.into_inner().number;
        let result = number * number;
        Ok(Response::new(SquareResponse { result }))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051".parse()?;
    let calculator = MyCalculator::default();

    println!("Server running on {:?}", addr);

    Server::builder()
        .add_service(CalculatorServer::new(calculator))
        .serve(addr)
        .await?;

    Ok(())
}

This code sets up a basic gRPC server that listens on localhost port 50051 and implements the Calculator service we defined earlier. When a client calls the Square method, it calculates the square of the provided number and returns the result.

Step 6: Build and Run

Build the project by running:

cargo build

Run the server:

cargo run

Creating a gRPC Client

With the server in place, let's create a simple Rust client to communicate with it.

Step 1: Create the Client

Create a new Rust project for the client:

cargo new grpc_calculator_client
cd grpc_calculator_client

Step 2: Update Cargo.toml

Open the Cargo.toml file and add the following dependencies:

[dependencies]
tonic = "0.5"
protobuf = "2"

Step 3: Implement the Client

Create a file named main.rs and add the following code to implement the gRPC client:

use tonic::transport::Channel;
use calculator::calculator_client::CalculatorClient;
use calculator::SquareRequest;

pub mod calculator {
    tonic::include_proto!("calculator");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "http://[::1]:50051".parse()?;
    let channel = Channel::from_shared(addr)?
        .connect()
        .await?;

    let mut client = CalculatorClient::new(channel);

    let request = tonic::Request::new(SquareRequest { number: 5 });
    let response = client.square(request).await?;

    println!("Square of 5 is: {}", response.into_inner().result);

    Ok(())
}

This code creates a gRPC client that connects to the server we previously implemented. It sends a request to calculate the square of the number 5 and prints the result.

Step 4: Build and Run the Client

Build the client by running:

cargo build

Run the client:

cargo run

You should see the client sending a request to the server and receiving the calculated square value, which should be 25 in this case.

Congratulations! You've successfully set up a gRPC server and client using Rust. This is just the beginning of what you can do with gRPC and Rust, as gRPC provides many features for building efficient and scalable APIs. You can now extend this example to build more complex gRPC services and clients to suit your application's needs. Happy coding!