gRPC для Rust: библиотеки и примеры
gRPC — это высокопроизводительный фреймворк удалённого вызова процедур (RPC), разработанный Google. Он использует Protocol Buffers (protobuf) для сериализации данных и HTTP/2 для транспорта. Благодаря строгой типизации, двунаправленной потоковой передаче и низкой задержке gRPC стал стандартом для микросервисной архитектуры. В экосистеме Rust есть две основные библиотеки для работы с gRPC: Tonic и grpc-rs. В этой статье мы разберём их особенности и приведём практические примеры.
1. Tonic — современный и асинхронный
Tonic — самая популярная библиотека gRPC для Rust. Она построена на основе tokio и hyper, полностью асинхронна и отлично интегрируется с экосистемой Rust. Tonic поддерживает все четыре типа gRPC-вызовов: унарные, серверный стриминг, клиентский стриминг и двунаправленный стриминг.
Установка и настройка
Добавьте зависимости в Cargo.toml:
[dependencies]
tonic = "0.12"
prost = "0.13"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
tonic-build = "0.12"
Определение protobuf-сервиса
Создайте файл proto/hello.proto:
syntax = "proto3";
package hello;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Сборка protobuf
В build.rs укажите:
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/hello.proto")?;
Ok(())
}
Реализация сервера
Пример простого gRPC-сервера на Tonic:
use tonic::{transport::Server, Request, Response, Status};
use hello::greeter_server::{Greeter, GreeterServer};
use hello::{HelloRequest, HelloReply};
pub mod hello {
tonic::include_proto!("hello");
}
#[derive(Default)]
pub struct MyGreeter;
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> {
let name = request.into_inner().name;
let reply = HelloReply {
message: format!("Привет, {}!", name),
};
Ok(Response::new(reply))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
let greeter = MyGreeter::default();
Server::builder()
.add_service(GreeterServer::new(greeter))
.serve(addr)
.await?;
Ok(())
}
Клиент
use hello::greeter_client::GreeterClient;
use hello::HelloRequest;
pub mod hello {
tonic::include_proto!("hello");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
let request = tonic::Request::new(HelloRequest {
name: "Мир".into(),
});
let response = client.say_hello(request).await?;
println!("Ответ сервера: {}", response.into_inner().message);
Ok(())
}
2. grpc-rs — зрелая альтернатива
grpc-rs — библиотека от команды TiKV, построенная на основе C-библиотеки gRPC Core. Она обеспечивает максимальную совместимость с официальным gRPC C-фреймворком, но требует установки системных зависимостей (cmake, protobuf). grpc-rs поддерживает синхронные и асинхронные вызовы, но уступает Tonic в удобстве использования.
Пример сервера на grpc-rs
use grpc::{ServerBuilder, ServerHandlerContext, ServerRequestHandler, SingleResponse};
use std::sync::Arc;
use protobuf::RepeatedField;
mod hello;
use hello::{HelloRequest, HelloReply};
struct GreeterService;
impl ServerRequestHandler for GreeterService {
fn handle_request(
&self,
_ctx: &ServerHandlerContext,
req: ServerRequestSingle<HelloRequest>,
resp: ServerResponseUnarySink<HelloReply>,
) {
let name =