gRPC in Rust
Abdusami Rust gRPC abdusami.dev@aliyun.com
背景
最近在开发一个基于微服务架构的项目,最初将各种服务之间的调用设计为通过 HTTP API 的形式,因此每个服务节点都应该实现一个 Web 服务器,并已经确定使用 Actix web 来实现,虽然基本上没啥问题,但是因本人的原因向研究和尝试以下通过 gRPC 来进行服务调用,从此通过联合官方的文档和示例代码来实现了一个简单的 gRPC 服务端和客户端,
由于关于 gRPC 干系的基本只是不再本博客范围之内,因此直接上代码。
设置 crate
Cargo.toml- [package]
- name = "tonic-helloworld"
- version = "0.1.0"
- edition = "2021"
- # gRPC 客户端
- [[bin]]
- name = "server"
- path = "src/server.rs"
- # gRPC 服务端
- [[bin]]
- name = "client"
- path = "src/client.rs"
- [dependencies]
- tonic = "0.8" # gRPC 的 Rust 实现库
- prost = "0.11"
- tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
- [build-dependencies]
- tonic-build = "0.8"
复制代码 将设置完上述内容之后继承编写干系的实现
由于 tonic 是在 Rust Tokio 生态当中,因此依靠 tokio 很正常
编写 Proto
之后需要编写 gRPC 的 protobuf 文件来形貌接口,因此为了方便管理需要在 项目根目录 下创建一个名为 proto 的文件夹,如:等创建完项目之后,需要编写一个 protobuf 在 proto/helloworld.proto 的大致内容如下:
还是最著名的 Hello world 案例
- syntax = "proto3";
- package helloworld;
- // 定义一个服务,类似于 Rust 里
- service Greeter {
- // 接口名称 (参数列表) returns (返回值)
- rpc say_hello(RpcRequest) returns (RpcResponse);
- }
- // 定义请求参数 (函数参数)
- message RpcRequest{
- string content = 1;
- }
- // 定义响应体 (函数返回值)
- message RpcResponse {
- string content = 1;
- }
复制代码 等编写完上述接口生命之后,需要编写构建脚本
该构建脚本是指 Rust 里的 build.rs
构建
由于 gRPC 的 protobuf 是需要先进行编译, 因此 Rust 中也不破例,需要进行编写构建规则,就需要在 项目根目录下 创建一个 build.rs 文件并编辑其内容如下:- fn main() -> Result<(), Box<dyn std::error::Error>> {
-
- println!("tonic::build is compiling proto file ...");
- // compile_protos 函数的参数是我们编写的的 proto 文件的路径,可以是相对路径
- tonic_build::compile_protos("proto/helloworld.proto")?;
- Ok(())
- }
复制代码 服务端实现
然后再实现 gRPC 中服务端,首先需要在源码目录下创建两个文件如 Cargo.toml 中所形貌而那样,如下:- touch src/server.rs && touch src/client.rs
复制代码 通过上述命令在 src/ 目录下创建如下两个文件:
- server.rs : 是 gRPC 的服务端实现
- client.rs : 是 gRPC 的客户端 (调用端)
然后再编写其内容:- use tonic::{transport::Server, Request, Response, Status};
- use helloworld::greeter_server::{Greeter, GreeterServer};
- use helloworld::{RpcRequest, RpcResponse};
- pub mod helloworld {
- /// 此时的 `helloworld` 是在 proto 文件里定义的 `package` 值对应
- tonic::include_proto!("helloworld");
- }
- /// 定义一个自定义 struct, 若有必要,可以实现 App State , 就存储应用状态 (定义字段)
- #[derive(Debug, Default)]
- pub struct MyGreeter {}
- /// 为自己的 struct 实现在 proto 文件里所定义的 `Trait`, 如下面所见,是异步
- /// 因此需要通过 `#[tonic::async_trait]` 宏来修饰
- #[tonic::async_trait]
- impl Greeter for MyGreeter {
- /// 函数签名,有一个 `&self`
- async fn say_hello(
- &self,
- request: Request<RpcRequest>,
- ) -> Result<Response<RpcResponse>, Status> {
- // 函数内部实现
- println!("GOT a new request on say_hello with {:?}", request);
-
- // 构造返回值
- let reply = helloworld::RpcResponse {
- content: format!("this response from server"),
- };
-
- // 函数返回
- Ok(Response::new(reply))
- }
- }
- #[tokio::main]
- async fn main() -> Result<(), Box<dyn std::error::Error>> {
- // gRPC 服务端地址
- let address = "[::1]:50051".parse()?;
- let greeter = MyGreeter::default();
- println!("RPC Server is starting on {:?} ...", address);
-
- // 启动服务端
- let server = Server::builder()
- .add_service(GreeterServer::new(greeter)) // 注册定义和实现的接口
- .serve(address);
- println!("RPC Server is running ...");
- server.await?;
- Ok(())
- }
复制代码 客户端实现
接下来需要编辑 client.rs 的内容来长途调用服务端提供的接口,其内容如下:- use helloworld::greeter_client::GreeterClient;
- use helloworld::RpcRequest;
- pub mod helloworld{
- /// 同样引入 proto
- 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(RpcRequest{
- content: "this request is from client".to_owned()
- });
-
- // 调用函数并携带参数,等执行完之后获取返回值,该方式如调用本地方法一样,比较直观
- let response = client.say_hello(request).await?;
-
- println!("response is {:?}", response);
-
- Ok(())
- }
复制代码 启动和测试
首先需要启动 gRPC 服务端,使用如下命令:其输出内容如下:- rusty:~/Documents/projects/rs/tonic-helloworld$ cargo run --bin server
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
- Running `target/debug/server`
- RPC Server is starting on [::1]:50051 ...
- RPC Server is running ...
复制代码 如上述所见,服务端已经启动,接下来启动客户端来实现调用,如下命令:输出内容如下:- rusty:~/Documents/projects/rs/tonic-helloworld$ cargo run --bin client
- Compiling tonic-helloworld v0.1.0 (/home/xxx/Documents/projects/rs/tonic-helloworld)
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.04s
- Running `target/debug/client`
- response is Response { metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Wed, 12 Feb 2025 17:30:37 GMT", "grpc-status": "0"} }, message: RpcResponse { content: "this response from server" }, extensions: Extensions }
复制代码 终 !
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |