當前位置: 妍妍網 > 碼農

Tonic:基於Rust的高效能gRPC實作

2024-06-19碼農

Tonic 是一個用於實作gRPC客戶端和伺服器的Rust庫,支持async/await語法。它著重於高效能、互操作性和靈活性,是基於Rust的一個強大工具,可以用於生產環境的系統構建。在本文中,我將深入介紹Tonic的元件、特性以及如何快速開始使用。

什麽是gRPC?

gRPC 是一種高效能、開源、通用的遠端程序呼叫(RPC)框架,旨在有效地連線分布式系統。gRPC使用HTTP/2作為傳輸協定,並使用Protobuf(Protocol Buffers)作為介面描述語言。

Tonic的架構

Tonic主要由三個部份組成:

  1. 通用的gRPC實作 :支持任何HTTP/2實作和透過一系列通用trait來實作的任何編碼。

  2. 高效能的HTTP/2實作 :基於hyper庫,這是一個構建在堅固的tokio棧上的HTTP/1.1和HTTP/2客戶端和伺服器。

  3. 基於prost的程式碼生成工具 :用於從protobuf定義中構建客戶端和伺服器。

主要功能

  • 雙向流 :支持同時進行的客戶端和伺服器流。

  • 高效能Async IO :利用Rust的async/await語法實作高效能異步I/O操作。

  • 互操作性 :支持與其他gRPC實作的互操作。

  • TLS支持 :基於rustls的TLS支持。

  • 負載均衡 :內建負載均衡功能。

  • 自訂後設資料 :可以在請求中添加自訂後設資料。

  • 認證 :支持多種身份驗證機制。

  • 健康檢查 :內建健康檢查功能。

  • 安裝和配置

    Tonic的最低支持Rust版本(MSRV)是1.70。以下是不同作業系統上的基本安裝步驟:

    Ubuntu

    sudo apt update && sudo apt upgrade -y
    sudo apt install -y protobuf-compiler libprotobuf-dev

    Alpine Linux

    sudo apk add protoc protobuf-dev

    macOS

    確保已經安裝了Homebrew。

    brew install protobuf

    Windows

    從此處下載最新版本的 protoc-xx.y-win64.zip ,解壓並將其路徑添加到系統PATH中。然後在命令提示字元中驗證安裝:

    protoc --version

    快速開始

    Tonic提供了豐富的範例以幫助你快速入門,包括一個簡單的「hello world」和更復雜的「routeguide」範例。接下來我們將展示如何建立一個基本的gRPC服務。

    建立計畫

    首先,建立一個新計畫:

    cargo new tonic-hello-world
    cd tonic-hello-world

    Cargo.toml 中添加Tonic依賴:

    [dependencies]
    tonic = "x.x.x"
    prost = "x.x.x"
    tokio = { version = "x.x", features = ["full"] }

    建立Protobuf定義

    建立一個Protobuf檔 proto/helloworld.proto

    syntax = "proto3";
    package helloworld;
    service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
    }
    message HelloRequest {
    string name = 1;
    }
    message HelloReply {
    string message = 1;
    }


    Cargo.toml 中配置Tonic構建外掛程式:

    [build-dependencies]
    tonic-build = "x.x.x"

    編寫構建指令碼

    建立一個 build.rs 檔以生成Rust程式碼:

    fnmain() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::compile_protos("proto/helloworld.proto")?;
    Ok(())
    }

    實作gRPC服務

    建立一個 src/main.rs 檔並實作服務:

    use tonic::{transport::Server, Request, Response, Status};
    use hello_world::greeter_server::{Greeter, GreeterServer};
    use hello_world::{HelloRequest, HelloReply};
    use std::sync::Arc;
    pubmod hello_world {
    tonic::include_proto!("helloworld");
    }
    #[derive(Default)]
    pubstructMyGreeter {}
    #[tonic::async_trait]
    impl Greeter for MyGreeter {
    asyncfnsay_hello(
    &self,
    request: Request<HelloRequest>,
    ) -> Result<Response<HelloReply>, Status> {
    println!("Got a request: {:?}", request);
    let reply = hello_world::HelloReply {
    message: format!("Hello {}!", request.into_inner().name).into(),
    };
    Ok(Response::new(reply))
    }
    }
    #[tokio::main]
    asyncfnmain() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051".parse()?;
    let greeter = MyGreeter::default();
    println!("GreeterServer listening on {}", addr);
    Server::builder()
    .add_service(GreeterServer::new(greeter))
    .serve(addr)
    .await?;
    Ok(())
    }







    執行服務

    在計畫根目錄下執行以下命令以生成Rust程式碼並啟動服務:

    cargo build
    cargo run

    現在,你的gRPC服務已經在 [::1]:50051 上執行了!

    進階功能

    雙向流

    Tonic支持雙向流,這對於即時通訊套用非常重要。以下是一個簡單的雙向流實作範例:

    use std::pin::Pin;
    use tonic::{Request, Response, Status};
    use tonic::transport::Server;
    use tonic::Request::Stream;
    use tokio_stream::Stream as TokioStream;
    use futures_core::Stream;
    use tokio_stream::wrappers::ReceiverStream;
    pubmod chat {
    tonic::include_proto!("chat");
    }
    #[derive(Default)]
    pubstructChatService {}
    #[tonic::async_trait]
    impl chat::chat_server::Chat for ChatService {
    typeChatStream = Pin<Box<dyn TokioStream<Item = Result<chat::ChatReply, Status>> + Send>>;
    asyncfnchat(
    &self,
    request: Request<Stream<chat::ChatRequest>>,
    ) -> Result<Response<Self::ChatStream>, Status> {
    println!("Chat request received");
    letmut stream = request.into_inner();
    let (tx, rx) = tokio::sync::mpsc::channel(4);
    tokio::spawn(asyncmove {
    whileletSome(req) = stream.message().await.unwrap() {
    let reply = chat::ChatReply {
    message: format!("RE: {}", req.message),
    };
    tx.send(Ok(reply)).await.unwrap();
    }
    });
    Ok(Response::new(Box::pin(ReceiverStream::new(rx)) as Self::ChatStream))
    }
    }
    #[tokio::main]
    asyncfnmain() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50052".parse().expect("Failed to parse address");
    let chat_service = ChatService::default();
    println!("ChatService listening on {}", addr);
    Server::builder()
    .add_service(chat::chat_server::ChatServer::new(chat_service))
    .serve(addr)
    .await?;
    Ok(())
    }











    結論

    Tonic透過Rust的高效能和async/await特性,為實作高效的gRPC通訊提供了強大的支持。無論是簡單的RPC呼叫還是復雜的雙向流通訊,Tonic都展示了其在高效能、互操作性和靈活性方面的優秀表現。

    文章精選

    「Rust