文章目录
1. 创建项目
cargo new --lib grpc-stream-demo
2. 配置proto
进入项目grpc-stream-demo
2.1 配置Cargo.toml, 内容如下:
[package]
name = "grpc-stream-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
tonic = "0.12.3"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "full"] }
prost = "0.13.3"
tokio-stream = "0.1"
futures = "0.3"
[build-dependencies]
tonic-build = "0.11.0"
2.2 创建文件proto/greeter.proto, 内容如下:
syntax = "proto3";
package greeter;
// The greeting service definition.
service Greeter {
// Sends a stream of greetings
rpc SayHello (HelloRequest) returns (stream HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
2.3 添加build.rs文件, 内容如下:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let protos = [
"proto/greeter.proto",
];
tonic_build::configure().compile(&protos, &["proto/"])?;
Ok(())
}
2.4 项目结构如下:
2.5 编译proto文件
cargo build
3.0 处理服务
3.1 项目引入
编辑src/lib.rs, 内容如下:
pub mod hello {
tonic::include_proto!("greeter");
}
3.2 添加src/server.rs, 内容如下:
HelloService 是实现hello的service
use tonic::{transport::Server, Request, Response, Status};
use futures::Stream; // Import Stream trait from futures crate
use tokio_stream::{wrappers::ReceiverStream};
use tokio::sync::mpsc;
use std::pin::Pin;
use tonic::async_trait;
use grpc_stream_demo::hello::greeter_server::{Greeter, GreeterServer};
use grpc_stream_demo::hello::{HelloRequest, HelloReply};
#[derive(Debug, Default)]
pub struct MyGreeter;
#[async_trait]
impl Greeter for MyGreeter {
type SayHelloStream = Pin<Box<dyn Stream<Item = Result<HelloReply, Status>> + Send>>;
// Implement the streaming SayHello method
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<Self::SayHelloStream>, Status> {
let name = request.into_inner().name;
// Create a channel for streaming responses
let (tx, rx) = mpsc::channel(4);
// Spawn a task to send greetings through the channel
tokio::spawn(async move {
for i in 1..=5 {
let message = format!("Hello, {}! This is greeting number {}", name, i);
let reply = HelloReply { message };
// Send each reply via the channel
if let Err(_) = tx.send(Ok(reply)).await {
break;
}
// Simulate a delay between greetings
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
});
// Convert the receiver into a stream
let stream = ReceiverStream::new(rx);
// Return the stream as the response
Ok(Response::new(Box::pin(stream)))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse().unwrap();
let greeter = MyGreeter::default();
println!("Greeter server listening on {}", addr);
Server::builder()
.add_service(GreeterServer::new(greeter))
.serve(addr)
.await?;
Ok(())
}
3.3 添加src/client.rs, 内容如下:
HelloService 是实现hello的service
use grpc_stream_demo::hello::greeter_client::GreeterClient;
use grpc_stream_demo::hello::{HelloRequest, HelloReply};
#[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: "Alice".into(),
});
let mut stream = client.say_hello(request).await?.into_inner();
while let Some(reply) = stream.message().await? {
println!("Received: {}", reply.message);
}
Ok(())
}
5 运行服务
5.1 修改Cargo.toml, 添加运行bin配置, 内容如下:
[package]
name = "grpc-stream-demo"
version = "0.1.0"
edition = "2021"
[[bin]]
name="client"
path="src/client.rs"
[[bin]]
name="server"
path="src/server.rs"
[dependencies]
tonic = "0.12.3"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "full"] }
prost = "0.13.3"
tokio-stream = "0.1"
futures = "0.3" # Add this line for futures
[build-dependencies]
tonic-build = "0.11.0"
5.2 运行server
cargo run --bin server
结果如下:
5.3 运行client, 测试
重新打开一个命令行, 进入项目根目录, 然后执行:
cargo run --bin client
结果如下: