跨语言调用
借助 PRC,比如 gPRC 技术,可以实现 Rust 和包括 Go 在内的其它语言进行跨语言调用。Rust 开发 gRPC 服务端和客户端
本章将使用 rust 来开发 gPRC 服务端和客户端Go 开发 gRPC 服务端和客户端
本章讨论 Go 开发 gRPC 服务端和客户端
Rust 开发 gRPC 服务端和客户端
Rust 开发 gRPC 主要依赖两个 crate:
tonic 依赖 prost(和axum),它们都提供了 build 工具,用于将 proto
生成 rust 代码。同样,tonic-build
也依赖于 prost-build
。
# Cargo.toml
[dependencies]
tokio = {version = "1", features = ["full"]}
prost = "0.11"
tonic = "0.8"
[build-dependencies]
tonic-build = "0.8"
构建文件
// build.rs
fn main() {
tonic_build::configure()
.out_dir("src/pb") // 生成代码的存放目录
.compile(
&["../proto/helloworld.proto"], // 欲生成的 proto 文件列表
&["../proto"], // proto 依赖所在的根目录
)
.unwrap();
}
生成代码
在 src
目录下创建 pb
目录:
mkdir src/pb
因为有 build.rs
,所以运行 cargo run
,将自动生成 proto 对应的 rust 文件。
cargo run
现在,src/pb
下应该有一个helloworld.rs
,这就是 tonic-build
由 proto/helloworld.proto
生成的对应的的 rust 代码。由于 rust 模块管理的要求,请创建 src/pb/mod.rs
,并输入以下内容:
pub mod helloworld;
多入口
我们采用最简单的多入口模式,将服务端和客户端以单独的文件放在 src
目录下。
进入 src
目录,并删除main.rs
:
cd src && rm main.rs
创建 server.rs
和client.rs
:
touch server.rs && touch client.rs
在 Cargo.toml
中配置多入口:
服务端
打开 src/server.rs
,开始编写服务端代码。
首先,我们定义一个自己的 MyGreeter
服务,并实现 tonic-build 根据 helloworld.proto 生成的代码中的 Greeter
:
#[derive(Default)]
pub struct MyGreeter {}
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: tonic::Request<pb::helloworld::HelloRequest>,
) -> Result<tonic::Response<pb::helloworld::HelloReply>, tonic::Status> {
println!("从 {:?} 获取到一个请求", request.remote_addr());
let reply = pb::helloworld::HelloReply {
message: format!("[Rust] 你好 {}!", request.into_inner().name),
};
Ok(tonic::Response::new(reply))
}
}
接着,在 main()
函数中启用这个服务:
#[tokio::main]
async fn main() {
let addr = "127.0.0.1:19527";
let greeter = MyGreeter::default();
println!("GreeterServer listening on {}", addr);
tonic::transport::Server::builder()
.add_service(GreeterServer::new(greeter))
.serve(addr.parse().unwrap())
.await
.unwrap();
}
启动服务端:
cargo run --bin server
客户端
客户端代码位于 src/client.rs
,相较于服务端代码,它简单许多:
// src/client.rs
use pb::helloworld::{greeter_client::GreeterClient, HelloRequest};
use std::env;
mod pb;
#[tokio::main]
async fn main() {
let grpc_server_addr = env::var("GRPC_SERVER").unwrap_or("127.0.0.1:19527".to_string());
let grpc_server_addr = format!("http://{}", grpc_server_addr);
let mut client = GreeterClient::connect(grpc_server_addr).await.unwrap();
let request = tonic::Request::new(HelloRequest {
name: "李四".into(),
});
let response = client.say_hello(request).await.unwrap();
println!("{:?}", response);
}
为了让客户端能连接 Rust 编写的服务端和 Go 编写的服务端,我们通过环境变量 GRPC_SERVER
来指定要连接服务端地址。
启动客户端:
cargo run --bin client
指定要连接的服务端地址:
GRPC_SERVER=127.0.0.1:19527 cargo run --bin client