gRPC 是开发中常用的开源高性能远程过程调用(RPC)框架,tonic 是基于 HTTP/2 的 gRPC 实现,专注于高性能、互操作性和灵活性。该库的创建是为了对 async/await 提供一流的支持,并充当用 Rust 编写的生产系统的核心构建块。今天我们聊聊通过使用tonic 调用grpc的的具体过程。
工程规划
rpc程序一般包含server端和client端,为了方便我们把两个程序打包到一个工程里面 新建tonic_sample工程
cargo new tonic_sample
Cargo.toml 如下
[package]
name = "tonic_sample"
version = "0.1.0"
edition = "2021"
[[bin]] # Bin to run the gRPC server
name = "stream-server"
path = "src/stream_server.rs"
[[bin]] # Bin to run the gRPC client
name = "stream-client"
path = "src/stream_client.rs"
[dependencies]
tokio.workspace = true
tonic = "0.9"
tonic-reflection = "0.9.2"
prost = "0.11"
tokio-stream = "0.1"
async-stream = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rand = "0.7"
h2 = { version = "0.3" }
anyhow = "1.0.75"
futures-util = "0.3.28"
[build-dependencies]
tonic-build = "0.9"
tonic 的示例代码还是比较齐全的,本次我们参考 tonic 的 streaming example。
首先编写 proto 文件,用来描述报文。 proto/echo.proto
syntax = "proto3";
package stream;
// EchoRequest is the request for echo.
message EchoRequest { string message = 1; }
// EchoResponse is the response for echo.
message EchoResponse { string message = 1; }
// Echo is the echo service.
service Echo {
// UnaryEcho is unary echo.
rpc UnaryEcho(EchoRequest) returns (EchoResponse) {}
// ServerStreamingEcho is server side streaming.
rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {}
// ClientStreamingEcho is client side streaming.
rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {}
// BidirectionalStreamingEcho is bidi streaming.
rpc BidirectionalStreamingEcho(stream EchoRequest)
returns (stream EchoResponse) {}
}
文件并不复杂,只有两个 message 一个请求一个返回,之所以选择这个示例是因为该示例包含了rpc中的流式处理,包扩了server 流、client 流以及双向流的操作。 编辑build.rs 文件
use std::{
env, path::PathBuf};
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/echo.proto")?;
Ok(())
}
该文件用来通过 tonic-build 生成 grpc 的 rust 基础代码
完成上述工作后就可以构建 server 和 client 代码了
stream_server.rs
pub mod pb {
tonic::include_proto!("stream");
}
use anyhow::Result;
use futures_util::FutureExt;
use pb::{
EchoRequest, EchoResponse};
use std::{
error::Error,
io::ErrorKind,
net::{
SocketAddr, ToSocketAddrs},
pin::Pin,
thread,
time::Duration,
};
use tokio::{
net::TcpListener,
sync::{
mpsc,
oneshot::{
self, Receiver, Sender},
Mutex,
},
task::{
self, JoinHandle},
};
use tokio_stream::{
wrappers::{
ReceiverStream, TcpListenerStream},
Stream, StreamExt,
};
use tonic::{
transport::Server, Request, Response, Status, Streaming};
type EchoResult<T> = Result<Response<T>, Status>;
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send>>;
fn match_for_io_error(err_status: &Status) -> Option<&std::io::Error> {
let mut err: &(dyn Error + 'static) = err_status;
loop {
if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
return Some(io_err);
}
// h2::Error do not expose std::io::Error with `source()`
// https://github.com/hyperium/h2/pull/462
if let Some(h2_err) = err.downcast_ref::<h2::Error>() {
if let Some(io_err) = h2_err.get_io() {
return Some(io_err);
}
}

本文详细介绍了如何使用Rust语言的tonic库开发gRPC服务,包括创建server和client,处理不同类型的流式请求(如unary、serverstreaming、clientstreaming和bidirectionalstreaming),以及如何添加gRPCreflectionAPI支持。
最低0.47元/天 解锁文章
2256

被折叠的 条评论
为什么被折叠?



