Rust gRPC with Tonic

Rust 에서 gRPC 해보기

오늘의 질문:

  • 러스트에서 gRPC를 사용하려면 어떻게 해야 할까?

오늘의 목표:

  • 러스트로 간단한 gRPC 서버 클라이언트 만들기

오늘의 참고 자료

나온지 1-2년 정도 된 따끈한 영상이라서 그런지 여전히 잘 돌아간다.

영상을 따라한다고 바로 돌아가는 것은 아니고, GitHub 패치가 되어 있어 레포를 클론하면 바로 동작한다.


proto

syntax = "proto3";

package calculator;

service Calculator {
  rpc Add(CalculationRequest) returns (CalculationResponse);
  rpc Divide(CalculationRequest) returns (CalculationResponse);
}

message CalculationRequest {
  int64 a = 1;
  int64 b = 2;
}

message CalculationResponse { int64 result = 1; }

service Admin {
  rpc GetRequestCount(GetCountRequest) returns (CounterResponse);
}

message GetCountRequest {}

message CounterResponse { uint64 count = 1; }

위와 같이 간단한 calculator proto 파일을 정의한다.

Calculator 서비스는 2개의 메서드가 있는데

  • 64비트 정수 a, b를 포함하는 CalculationRequest를 인자로 받고
  • int64 정수 반환 값 한 개로 이루어진 CalcualtionResponse를 반환한다.

여기까지는 gRPC를 사용할 때 언어 무관하게 공통적으로 셋업해주어야 하는 부분이다.


build.rs

use std::error::Error;
use std::{env, path::PathBuf};

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

    tonic_build::configure()
        .file_descriptor_set_path(out_dir.join("calculator_descriptor.bin"))
        .compile(&["proto/calculator.proto"], &["proto"])?;

    tonic_build::compile_protos("proto/calculator.proto")?;

    Ok(())
}
# Cargo.toml
[build-dependencies]
tonic-build = "0.11"

다른 cargo 프로젝트와 조금 다른 점이 있다면, 빌드를 하는 과정에 protoc이라는 외부 디펜던시를 사용하는 과정이 포함된다는 것이다.

따라서 cargo build 만으로 해결되는 것이 아니라, 따로 해당 protoc을 실행시켜 proto 파일을 컴파일하는 과정을 기술한 build.rs 또한 추가해야 한다. tonic_build 라는 모듈에서 이를 쉽게 해줄 수 있는 api를 제공한다.


gRPC Client

use proto::calculator_client::CalculatorClient;
use std::error::Error;

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let url = "http://[::1]:50051";
    let mut client = CalculatorClient::connect(url).await?;

    let req = proto::CalculationRequest { a: 4, b: 5 };
    let request = tonic::Request::new(req);

    let response = client.add(request).await?;

    println!("Response: {:?}", response.get_ref().result);

    Ok(())
}

pub mod proto {}를 선언하면, tonic crate에서 자동으로 calculator 와 관련된 struct과 함수들을 자동으로 선언해주므로,
calculator와 관련된 add, Request 등의 함수들을 바로 사용할 수 있게 된다.

위 코드에서 보이는 것처럼 Calculatorclient를 바로 사용할 수 있다.

/// Generated client implementations

이미지의 위 주석에서 보이는 것처럼
pub struct CalculatorClient를 만들어준다.


gRPC Server

서버는 조금 더 복잡하다.

client 에서 보았듯이, calculator_server가 자동으로 생성된다.

calculator_server 로 동작하기 위해서는 우선, Calculator로서 동작하는 어떤 실체를 만들어야 한다.

자동으로 생성된 calculator.rs 를 보면 proto 파일에 정의된 형태의 trait가 만들어진 것을 볼 수 있다. 위 trait를 구현한 어떤 서비스를 만들면 Calculator server에 등록할 수 있다.

위와 같이 Calculator 를 impl 하는 CalculatorService 를 완성해 준 다음,

main 함수에서 이를 생성하고

CalculatorService를 이용해 CalculatorServer를 생성하고,
이 생성된 서버를 add_service 함수를 통해 등록해주면, grpc 서버로서 Calculator 서비스를 서빙하게 된다.


With Web

gRPC의 강점은 역시 언어에 구애받지 않고, 바로 다른 종류의 언어를 사용하는 클라이언트에서 사용할 수 있다는 점이 있다.

https://github.com/dreamsofcode-io/grpcalculator-web

위 레포를 받고

vite run dev
npm run dev
bun run dev

셋 중 아무거나 원하는 걸로 실행을 하면 간단한 웹 페이지가 뜨게 된다.

간단하고 흔한 react 웹 페이지인데, 사용법이 이렇게 간단하다.

grpc transport에 연결하고, CalculatorClient를 생성하고, 거기에 add 함수를 실행시키면 원격으로 해당 함수가 실행된다.

이렇게 서버로부터 결과를 잘 받아오는 것을 확인할 수 있다.


Rust에서 간단한 gRPC 사용법을 알아봤다. 매우 간단해서 편하게 사용할 수 있다.

0개의 댓글