tonic是基于HTTP/2的gRPC实现,专注于高性能,互通性和灵活性。创建该库的目的是为了对async/await具有一流的支持,并充当用Rust编写的生产系统的核心构建块。rustls进行TLS加密支持rustup,可以尝试使用它快速方便的安装Rust.$ cargo new helloworld-tonic
$ cd helloworld-tonic
tonic需要rust1.39及以上版本,因为它需要async_await特性的支持。$ rustup update
$ rustup component add rustfmt
.proto放进crate根目录下的一个子目录中。需要注意的是,Tonic对.proto定义文件的位置并没有严格的要求。$ mkdir proto
$ touch proto/helloworld.proto
helloworldsyntax = "proto3";
package helloworld;
service Greeter {
// SayHello rpc 接受 HelloRequests 并返回 HelloReplies
rpc SayHello (HelloRequest) returns (HelloReply);
}
SayHello RPC方法中的请求与返回的类型。RPC类型定义为包含类型化字段的消息,如下:message HelloRequest {
// 请求消息中包含要问候的名称
string name = 1;
}
message HelloReply {
// 回复包含问候语
string message = 1;
}
.proto文件完整的样子:syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Cargo.toml中加入必要的依赖项。[package]
name = "helloworld-tonic"
version = "0.1.0"
edition = "2018"
[[bin]] # 用来运行 HelloWorld gRPC 服务器的可执行文件
name = "helloworld-server"
path = "src/server.rs"
[[bin]] # 用来运行 HelloWorld gRPC 客户端的可执行文件
name = "helloworld-client"
path = "src/client.rs"
[dependencies]
tonic = "0.3"
prost = "0.6"
tokio = { version = "0.2", features = ["macros"] }
[build-dependencies]
tonic-build = "0.3"
tonic-build是为了可以使用它在构建过程中很方便的生成gRPC的客户端和服务器端代码。以下是设置这个构建过程的方式:build.rs文件,然后添加以下代码:fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/helloworld.proto")?;
Ok(())
}
tonic-build来编译protobuf文件。配置这个构建过程的方式不止此一种,不过其他方式我们就不再这里赘述了。/src目录下创建一个叫server.rs的文件并加入以下代码开始:use tonic::{transport::Server, Request, Response, Status};
use hello_world::greeter_server::{Greeter, GreeterServer};
use hello_world::{HelloReply, HelloRequest};
pub mod hello_world {
tonic::include_proto!("helloworld"); // 这里指定的字符串必须与proto的包名称一致
}
.proto中定义的Greeter服务:#[derive(Debug, Default)]
pub struct MyGreeter {}
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<HelloRequest>, // 接收以HelloRequest为类型的请求
) -> Result<Response<HelloReply>, Status> { // 返回以HelloReply为类型的示例作为响应
println!("Got a request: {:?}", request);
let reply = hello_world::HelloReply {
message: format!("Hello {}!", request.into_inner().name).into(), // 由于gRPC请求和响应中的字段都是私有的,所以需要使用 .into_inner()
};
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 tonic::{transport::Server, Request, Response, Status};
use hello_world::greeter_server::{Greeter, GreeterServer};
use hello_world::{HelloReply, HelloRequest};
pub mod hello_world {
tonic::include_proto!("helloworld");
}
#[derive(Debug, Default)]
pub struct MyGreeter {}
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_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]
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(())
}
cargo run --bin helloworld-server来运行你的HelloWorld gRPC服务器。这里使用到了之前我们在Cargo.toml中定义的[[bin]]来运行服务器。$ grpcurl -plaintext -import-path ./proto -proto helloworld.proto -d '{"name": "Tonic"}' [::]:50051 helloworld.Greeter/SayHello
{
"message": "Hello Tonic!"
}
/src目录下创建一个叫client.rs的文件并引用所有需要的库开始:use hello_world::greeter_client::GreeterClient;
use hello_world::HelloRequest;
pub mod hello_world {
tonic::include_proto!("helloworld");
}
#[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: "Tonic".into(),
});
let response = client.say_hello(request).await?;
println!("RESPONSE={:?}", response);
Ok(())
}
use hello_world::greeter_client::GreeterClient;
use hello_world::HelloRequest;
pub mod hello_world {
tonic::include_proto!("helloworld");
}
#[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: "Tonic".into(),
});
let response = client.say_hello(request).await?;
println!("RESPONSE={:?}", response);
Ok(())
}
proto/helloworld.proto, build.rs,src/server.rs ,src/client.rscargo run --bin helloworld-server。然后在另一个终端窗口中运行客户端:cargo run --bin helloworld-client原文作者:Rust语言中文社区;如有侵权,请联系管理员删除。