Rust高性能gRPC库ttrpc的使用,ttrpc提供轻量级RPC框架支持跨平台通信
ttrpc-rust 是 containerd 的非核心子项目。
ttrpc-rust 是 ttrpc 的 Rust 版本。ttrpc 是用于低内存环境的 GRPC。
ttrpc-rust 的 ttrpc 编译器 ttrpc_rust_plugin 是从 gRPC-rs 的 gRPC 编译器 grpcio-compiler 修改而来。
用法
- 使用 protoc 命令生成
从 proto 文件生成源代码:
-
安装 protoc
-
安装 protobuf-codegen
cargo install --force protobuf-codegen
- 安装 ttrpc_rust_plugin
cd ttrpc-rust/compiler
cargo install --force --path .
- 生成源代码:
$ protoc --rust_out=. --ttrpc_out=. --plugin=protoc-gen-ttrpc=`which ttrpc_rust_plugin` example.proto
- 以编程方式生成
用于生成 .rs 文件的 API,例如从 build.rs 中使用。
示例代码:
fn main() {
protoc_rust_ttrpc::Codegen::new()
.out_dir("protocols")
.inputs(&[
"protocols/protos/agent.proto",
])
.include("protocols/protos")
.rust_protobuf() // 同时生成 protobuf 消息,不仅仅是服务
.run()
.expect("Codegen failed.");
}
async/.await
ttrpc-rust 支持 async/.await。通过使用 async/.await,可以减少由线程引起的开销和资源消耗。
用法
- 生成异步版本的代码
目前我们仅支持使用 ttrpc-codegen 生成异步代码
ttrpc_codegen::Codegen::new()
.out_dir("protocols/asynchronous")
.inputs(&protos)
.include("protocols/protos")
.rust_protobuf()
.customize(Customize {
gen_mod: true, // Gen mod 将在 out_dir 中添加 mod.rs。它与 protobuf 的 gen_mod_rs 兼容
async_all: true, // 这是关键选项。
..Default::default()
})
.run()
.expect("Gen async codes failed.");
提供自定义选项
async_all: 为服务器和客户端生成异步代码 async_server: 为服务器生成异步代码 async_client: 为客户端生成异步代码 gen_mod: 在 out_dir 中生成 mod.rs
请参阅 example/build.rs 中的更多内容
- 以 async/.await 的方式编写您的实现
请遵循 example/async-server.rs 和 example/async-client.rs 中的指南
运行示例
- 进入目录
$ cd ttrpc-rust/example
- 启动服务器
$ cargo run --example server
或
$ cargo run --example async-server
- 启动客户端
$ cargo run --example client
或
$ cargo run --example async-client
注意:protobuf 的版本
protobuf-codegen、ttrpc_rust_plugin 和您的代码应使用相同版本的 protobuf。 如果使用不同版本的 protobuf,将出现以下错误:
27 | const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_8_0;
| ^^^^^^^^^^^^^ help: a constant with a similar name exists: `VERSION_2_10_1`
原因是 protobuf-codegen 生成的文件仅与相同版本的运行时兼容
要解决此问题:
- 使用新的 protobuf 重新构建 protobuf-codegen:
cd grpc-rs
cargo clean
cargo update
cargo install --force protobuf-codegen
- 使用新的 protobuf 重新构建 ttrpc_rust_plugin:
cd ttrpc-rust/compiler
cargo clean
cargo update
cargo install --force --path .
- 构建您的项目。
完整示例代码:
// build.rs
fn main() {
// 生成同步版本代码
protoc_rust_ttrpc::Codegen::new()
.out_dir("src/protocols")
.inputs(&["protos/example.proto"])
.include("protos")
.rust_protobuf()
.run()
.expect("Codegen failed.");
// 生成异步版本代码
ttrpc_codegen::Codegen::new()
.out_dir("src/protocols_async")
.inputs(&["protos/example.proto"])
.include("protos")
.rust_protobuf()
.customize(ttrpc_codegen::Customize {
gen_mod: true,
async_all: true,
..Default::default()
})
.run()
.expect("Gen async codes failed.");
}
// src/main.rs
mod protocols;
mod protocols_async;
use ttrpc::context;
fn main() {
// 同步客户端示例
let client = protocols::example::ExampleClient::new("unix:///tmp/example.sock").unwrap();
let ctx = context::Context::new();
let request = protocols::example::Request::new();
let response = client.echo(ctx, &request).unwrap();
println!("Response: {:?}", response);
// 异步客户端示例
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let client = protocols_async::example::ExampleClient::new("unix:///tmp/example.sock").unwrap();
let ctx = context::Context::new();
let request = protocols_async::example::Request::new();
let response = client.echo(ctx, &request).await.unwrap();
println!("Async response: {:?}", response);
});
}
// 服务器实现示例
struct ExampleService;
impl protocols::example::Example for ExampleService {
fn echo(&self, _ctx: &ttrpc::context::Context, req: &protocols::example::Request)
-> ttrpc::Result<protocols::example::Response>
{
let mut resp = protocols::example::Response::new();
resp.set_message(req.get_message().to_string());
Ok(resp)
}
}
// 异步服务器实现示例
struct AsyncExampleService;
#[async_trait::async_trait]
impl protocols_async::example::Example for AsyncExampleService {
async fn echo(&self, _ctx: &ttrpc::context::Context, req: &protocols_async::example::Request)
-> ttrpc::Result<protocols_async::example::Response>
{
let mut resp = protocols_async::example::Response::new();
resp.set_message(req.get_message().to_string());
Ok(resp)
}
}
ttrpc:Rust高性能gRPC库使用指南
概述
ttrpc是一个基于Rust语言开发的高性能gRPC库,提供轻量级RPC框架,支持跨平台通信。该库专为需要高效网络通信的场景设计,具有低延迟、高吞吐量的特点,同时保持代码简洁和易于使用。
主要特性
- 高性能异步通信
- 跨平台支持(Linux、Windows、macOS)
- Protocol Buffers协议支持
- 轻量级设计,依赖较少
- 完整的gRPC功能实现
安装方法
在Cargo.toml中添加依赖:
[dependencies]
ttrpc = "0.6"
prost = "0.11"
tokio = { version = "1.0", features = ["full"] }
基本使用方法
1. 定义Proto文件
首先创建proto文件定义服务:
syntax = "proto3";
package example;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
2. 生成Rust代码
使用prost-build生成Rust代码:
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
prost_build::compile_protos(&["src/example.proto"], &["src/"])?;
Ok(())
}
3. 实现服务端
use ttrpc::{self, asynchronous::Server};
use example::{Greeter, HelloRequest, HelloReply};
use async_trait::async_trait;
#[derive(Clone)]
struct GreeterService;
#[async_trait]
impl Greeter for GreeterService {
async fn say_hello(
&self,
_ctx: &ttrpc::r#async::Context,
req: HelloRequest,
) -> ttrpc::Result<HelloReply> {
let message = format!("Hello, {}!", req.name);
Ok(HelloReply { message })
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let greeter = GreeterService;
let service = example::create_greeter(greeter);
let mut server = Server::new()
.bind("unix:///tmp/ttrpc_example.sock")?
.register_service(service);
server.start().await?;
Ok(())
}
4. 实现客户端
use ttrpc::{self, asynchronous::Client};
use example::{GreeterClient, HelloRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::connect("unix:///tmp/ttrpc_example.sock")?;
let greeter_client = GreeterClient::new(client);
let request = HelloRequest {
name: "World".to_string(),
};
let response = greeter_client.say_hello(ttrpc::context::empty(), &request).await?;
println!("Response: {}", response.message);
Ok(())
}
高级配置
自定义服务器选项
use ttrpc::asynchronous::Server;
use ttrpc::ServerOption;
let options = ServerOption {
work_threads: 4,
..Default::default()
};
let server = Server::with_option(options)
.bind("tcp://0.0.0.0:50051")?
.register_service(service);
错误处理示例
async fn say_hello(
&self,
_ctx: &ttrpc::r#async::Context,
req: HelloRequest,
) -> ttrpc::Result<HelloReply> {
if req.name.is_empty() {
return Err(ttrpc::Error::RpcStatus(ttrpc::Status::new(
ttrpc::Code::INVALID_ARGUMENT,
"Name cannot be empty",
)));
}
Ok(HelloReply {
message: format!("Hello, {}!", req.name),
})
}
性能优化建议
- 使用合适的线程池大小
- 启用TCP_NODELAY减少延迟
- 使用连接池管理客户端连接
- 合理设置消息大小限制
注意事项
- 确保proto文件定义与生成的Rust代码保持一致
- 在生产环境中使用适当的错误处理和日志记录
- 考虑使用TLS加密进行安全通信
- 监控服务器资源使用情况
ttrpc为Rust开发者提供了强大而高效的gRPC实现,特别适合需要高性能网络通信的应用程序。
完整示例demo
以下是一个完整的ttrpc使用示例,包含服务端和客户端的完整实现:
项目结构:
ttrpc-example/
├── Cargo.toml
├── build.rs
├── src/
│ ├── main.rs
│ └── example.proto
Cargo.toml:
[package]
name = "ttrpc-example"
version = "0.1.0"
edition = "2021"
[dependencies]
ttrpc = "0.6"
prost = "0.11"
tokio = { version = "1.0", features = ["full"] }
async-trait = "0.1"
prost-types = "0.11"
[build-dependencies]
prost-build = "0.11"
// 构建脚本,用于从proto文件生成Rust代码
fn main() -> Result<(), Box<dyn std::error::Error>> {
prost_build::compile_protos(&["src/example.proto"], &["src/"])?;
Ok(())
}
src/example.proto:
syntax = "proto3";
package example;
// 定义问候服务
service Greeter {
// 打招呼方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 请求消息
message HelloRequest {
string name = 1; // 姓名字段
}
// 响应消息
message HelloReply {
string message = 1; // 消息字段
}
src/main.rs:
// 生成proto文件对应的Rust代码
mod example {
include!(concat!(env!("OUT_DIR"), "/example.rs"));
}
use ttrpc::{self, asynchronous::{Server, Client}};
use example::{Greeter, HelloRequest, HelloReply, GreeterClient, create_greeter};
use async_trait::async_trait;
use std::error::Error;
// 服务实现结构体
#[derive(Clone)]
struct GreeterService;
#[async_trait]
impl Greeter for GreeterService {
// 实现say_hello方法
async fn say_hello(
&self,
_ctx: &ttrpc::r#async::Context,
req: HelloRequest,
) -> ttrpc::Result<HelloReply> {
// 检查名称是否为空
if req.name.is_empty() {
return Err(ttrpc::Error::RpcStatus(ttrpc::Status::new(
ttrpc::Code::INVALID_ARGUMENT,
"Name cannot be empty",
)));
}
// 构造响应消息
let message = format!("Hello, {}!", req.name);
Ok(HelloReply { message })
}
}
// 服务端主函数
#[tokio::main]
async fn server_main() -> Result<(), Box<dyn Error>> {
println!("Starting ttrpc server...");
// 创建服务实例
let greeter = GreeterService;
let service = create_greeter(greeter);
// 配置服务器选项
let options = ttrpc::ServerOption {
work_threads: 4,
..Default::default()
};
// 创建并启动服务器
let mut server = Server::with_option(options)
.bind("unix:///tmp/ttrpc_example.sock")?
.register_service(service);
println!("Server listening on unix:///tmp/ttrpc_example.sock");
server.start().await?;
Ok(())
}
// 客户端主函数
#[tokio::main]
async fn client_main() -> Result<(), Box<dyn Error>> {
println!("Starting ttrpc client...");
// 连接服务器
let client = Client::connect("unix:///tmp/ttrpc_example.sock")?;
let greeter_client = GreeterClient::new(client);
// 创建请求
let request = HelloRequest {
name: "World".to_string(),
};
// 发送请求并获取响应
let response = greeter_client
.say_hello(ttrpc::context::empty(), &request)
.await?;
println!("Server response: {}", response.message);
// 测试错误情况
let empty_request = HelloRequest {
name: "".to_string(),
};
match greeter_client
.say_hello(ttrpc::context::empty(), &empty_request)
.await
{
Ok(_) => println!("Unexpected success"),
Err(e) => println!("Expected error: {}", e),
}
Ok(())
}
// 主函数,可以选择运行服务端或客户端
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 && args[1] == "server" {
server_main().await
} else {
client_main().await
}
}
运行说明:
- 首先启动服务端:
cargo run -- server
- 然后在另一个终端运行客户端:
cargo run
这个完整示例展示了ttrpc的基本使用方法,包括:
- Proto文件的定义和代码生成
- 服务端的实现和配置
- 客户端的连接和调用
- 错误处理机制
- 异步通信的实现
通过这个示例,您可以快速开始使用ttrpc构建高性能的gRPC服务。