Rust gRPC 開發

在這篇文章中,我們將使用 gRPC 創建一個基本的 Todo 應用程序。首先,我們將非常快速的概述一下 gRPC 和 Protocol Buffers。

什麼是 gRPC?

gRPC 是一個現代的開源的高性能遠程過程調用 (RPC) 框架,可以在任何環境下運行。RPC 代表遠程過程調用(Remote Procedure Call),開頭的 g 代表通用(General Purpose),對某些人來說,它代表谷歌。

我假設你熟悉常見的 REST api,它們通過 JSON 對象進行通信。在 gRPC 中,我們使用 Protocol Buffers 來序列化數據,而不是 JSON。

Protocol Buffers

Protocol Buffers 是谷歌定製的用於語言無關、平臺無關、可擴展的,序列化結構性數據的一種協議。

在 gRPC 中,傳輸的 (序列化的) 數據是二進制的。這意味着它比 JSON 或 XML 更快,因爲它佔用更少的空間,而更少的空間帶來更小的帶寬。

Rust gRPC 開發

首先,創建一個新的工程:

cargo new rust-grpc

現在,在創建工程之後,我們爲 gRPC 添加一些依賴項,並在 Cargo.toml 中定義二進制程序,用於我們的服務器和客戶端:

[[bin]]
name = "grpc-server"
path = "src/server.rs"
[[bin]]
name = "grpc-client"
path = "src/client.rs"
[dependencies]
tonic = "0.7"
prost = "0.10"
tokio = { version = "1.19", features = ["rt-multi-thread", "macros"] }
[build-dependencies]
tonic-build = "0.7"

創建 proto 文件

在項目路徑下創建 proto/todo.proto:

syntax = "proto3";

import "google/protobuf/empty.proto";

package todo;

message TodoItem {
    string name = 1;
    string description = 2;
    int32 priority = 3;
    bool completed = 4;
}

message GetTodosResponse {
    repeated TodoItem todos = 1;
}

message CreateTodoRequest {
    string name = 1;
    string description = 2;
    int32 priority = 3;
}

message CreateTodoResponse {
    TodoItem todo = 1;
    bool status = 2;
}

service Todo {
    rpc GetTodos(google.protobuf.Empty) returns (GetTodosResponse);
    rpc CreateTodo(CreateTodoRequest) returns (CreateTodoResponse);
}

這是 Proto 文件的語法。讓我們大概看一下:

編譯 proto 文件

在項目目錄下創建 build.rs :

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::configure(
        .compile(&["proto/todo.proto"]&["proto"])
        .unwrap();

    Ok(())
}

目錄結構如下:

Server 端代碼

src/server.rs

use std::sync::Mutex;

use todo::{
    todo_server::{Todo, TodoServer},
    CreateTodoRequest, CreateTodoResponse, GetTodosResponse, TodoItem,
};
use tonic::transport::Server;

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

#[derive(Debug, Default)]
pub struct TodoService {
    todos: Mutex<Vec<TodoItem>>,
}

#[tonic::async_trait]
impl Todo for TodoService {
    async fn get_todos(
        &self,
        _: tonic::Request<()>,
    ) -> Result<tonic::Response<GetTodosResponse>, tonic::Status> {
        let message = GetTodosResponse {
            todos: self.todos.lock().unwrap().to_vec(),
        };

        Ok(tonic::Response::new(message))
    }

    async fn create_todo(
        &self,
        request: tonic::Request<CreateTodoRequest>,
    ) -> Result<tonic::Response<CreateTodoResponse>, tonic::Status> {
        let payload = request.into_inner();

        let todo_item = TodoItem {
            name: payload.name,
            description: payload.description,
            priority: payload.priority,
            completed: false,
        };

        self.todos.lock().unwrap().push(todo_item.clone());

        let message = CreateTodoResponse {
            todo: Some(todo_item),
            status: true,
        };

        Ok(tonic::Response::new(message))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "0.0.0.0:50051".parse().unwrap();
    let todo_service = TodoService::default();

    Server::builder()
        .add_service(TodoServer::new(todo_service))
        .serve(addr)
        .await?;

    Ok(())
}

讓我們快速看看發生了什麼:

Client 端代碼

src/client.rs

use crate::todo::{todo_client::TodoClient, CreateTodoRequest};

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

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

    let request = tonic::Request::new(());

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

    println!("{:?}", response.into_inner().todos);

    let create_request = tonic::Request::new(CreateTodoRequest {
        name: "test name".to_string(),
        description: "test description".to_string(),
        priority: 1,
    });

    let create_response = client.create_todo(create_request).await?;

    println!("{:?}", create_response.into_inner().todo);

    Ok(())
}

運行

cargo build
cargo run --bin grpc-server
cargo run --bin grpc-client

本文翻譯自:

https://betterprogramming.pub/how-to-create-grpc-server-client-in-rust-4e37692229f0

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/GCnd5wbPDVe-xMPWPVLYRA