其实比较好的思路是,server用rust写,client用其他语言写。这才是rpc的用处~
使用gRPC在Rust中创建一个简单的例子涉及几个步骤。这里我将为你提供一个简单的gRPC服务器和客户端的例子。为了实现这个,你需要以下几个组件:
Protobuf定义(.proto文件) :这定义了gRPC服务和消息。
Rust gRPC服务器 :它实现了.proto文件中定义的服务。
Rust gRPC客户端 :它调用服务器上的gRPC服务。
首先,确保你的环境安装了以下工具:
第一步:创建项目结构
创建一个新的Rust项目:
1 2 cargo new rust_grpc_example cd rust_grpc_example
在项目根目录创建一个proto文件夹,并在其中创建一个hello.proto文件。
第二步:编写.proto文件 在proto/hello.proto文件中写入以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 syntax = "proto3" ; package hello;service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1 ; } message HelloReply { string message = 1 ; }
第三步:添加依赖 在你的Cargo.toml文件中添加以下依赖:
1 2 3 4 5 6 7 [dependencies] tonic = "0.6" tokio = { version = "1" , features = ["full" ] }prost = "0.9" [build-dependencies] tonic-build = "0.6"
第四步:创建build脚本 在build.rs文件中,添加以下代码来生成Rust代码从.proto文件:
1 2 3 4 fn main () { tonic_build::compile_protos ("proto/hello.proto" ) .expect ("Failed to compile proto files" ); }
第五步:编写gRPC服务器 在src/main.rs中,创建一个gRPC服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 use tonic::{transport::Server, Request, Response, Status};pub mod hello { tonic::include_proto!("hello" ); } use hello::greeter_server::{Greeter, GreeterServer};use hello::{HelloReply, HelloRequest};#[derive(Default)] pub struct MyGreeter {}#[tonic::async_trait] impl Greeter for MyGreeter { async fn say_hello ( &self , request: Request<HelloRequest>, ) -> Result <Response<HelloReply>, Status> { let reply = hello::HelloReply { message: format! ("Hello {}!" , request.into_inner ().name), }; Ok (Response::new (reply)) } } #[tokio::main] async fn main () -> Result <(), Box <dyn std::error::Error>> { let addr = "[::1]:50051" .parse ()?; let greeter = MyGreeter::default (); println! ("GreeterServer listening on {}" , addr); Server::builder () .add_service (GreeterServer::new (greeter)) .serve (addr) .await ?; Ok (()) }
第六步:编写gRPC客户端 在同一个src/main.rs文件中,你可以添加一个客户端来测试服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #[tokio::main] async fn main () -> Result <(), Box <dyn std::error::Error>> { let mut client = hello::greeter_client::GreeterClient::connect ("http://[::1]:50051" ).await ?; let request = tonic::Request::new (HelloRequest { name: "world" .into (), }); let response = client.say_hello (request).await ?; println! ("RESPONSE={:?}" , response.into_inner ().message); Ok (()) }
编译和运行
在项目根目录执行cargo run来编译和运行项目。
服务器将启动并监听在[::1]:50051,客户端将发送一个请求并打印
两个fn main,无法编译通过吧
你是对的,Rust 程序不能有两个 main 函数。我之前的示例中同时包含了客户端和服务器代码,这在实际应用中是不可行的。通常,客户端和服务器应该是分开的程序。为了简化,我将展示如何分别为客户端和服务器创建单独的文件。
修改项目结构
在 src 目录下创建两个新文件:server.rs 和 client.rs。
server.rs 将服务器代码放入 server.rs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 use tonic::{transport::Server, Request, Response, Status};pub mod hello { tonic::include_proto!("hello" ); } use hello::greeter_server::{Greeter, GreeterServer};use hello::{HelloReply, HelloRequest};#[derive(Default)] pub struct MyGreeter {}#[tonic::async_trait] impl Greeter for MyGreeter { async fn say_hello ( &self , request: Request<HelloRequest>, ) -> Result <Response<HelloReply>, Status> { let reply = hello::HelloReply { message: format! ("Hello {}!" , request.into_inner ().name), }; Ok (Response::new (reply)) } } #[tokio::main] async fn main () -> Result <(), Box <dyn std::error::Error>> { let addr = "[::1]:50051" .parse ()?; let greeter = MyGreeter::default (); println! ("GreeterServer listening on {}" , addr); Server::builder () .add_service (GreeterServer::new (greeter)) .serve (addr) .await ?; Ok (()) }
client.rs 将客户端代码放入 client.rs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 use tonic::Request;pub mod hello { tonic::include_proto!("hello" ); } use hello::greeter_client::GreeterClient;use hello::HelloRequest;#[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: "world" .into (), }); let response = client.say_hello (request).await ?; println! ("RESPONSE={:?}" , response.into_inner ().message); Ok (()) }
修改Cargo.toml 在 Cargo.toml 文件中,指定这两个不同的二进制目标:
1 2 3 4 5 6 7 [[bin]] name = "server" path = "src/server.rs" [[bin]] name = "client" path = "src/client.rs"
这样,你就可以分别运行服务器和客户端了:
运行服务器:cargo run --bin server
运行客户端:cargo run --bin client
确保服务器在运行时启动客户端,以便客户端可以连接并发送请求。
本篇是对 Rust网络编程系列 学习与实践.
https://github.com/anonymousGiga/net/blob/master/use_grpc/Cargo.toml
一直报错: protobuf must have version 3
1 2 3 4 5 6 7 8 9 error: failed to run custom build command for `use_grpc v0.1.0 (/Users/fliter/net/use_grpc)` Caused by: process didn't exit successfully: `/Users/fliter/net/use_grpc/target/debug/build/use_grpc-96c98097edaccc88/build-script-build` (exit status: 101) --- stderr thread 'main' panicked at /Users/fliter/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/protoc-rust-grpc-0.7.1/src/lib.rs:107:13: protobuf must have version 3 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace warning: build failed, waiting for other jobs to finish...
需先安装protoc. Mac上使用brew install protobuf,Ubuntu可使用apt install -y protobuf-compiler
之后执行 protoc --version验证是否成功安装
cargo new use_grpc --lib
在src同级目录下创建foobar.proto文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 syntax = "proto3"; package foobar; service FooBarService { rpc record_cab_location(CabLocationRequest) returns (CabLocationResponse); rpc get_cabs(GetCabRequest) returns (GetCabResponse); } message CabLocationRequest { string name = 1; Location location = 2; } message CabLocationResponse { bool accepted = 1; } message GetCabRequest { Location location = 1; } message GetCabResponse { repeated Cab cabs = 1; } message Cab { string name = 1; Location location = 2; } message Location { float latitude = 1; float longitude = 2; }
在Cargo.toml的[dependencies]下新增
1 2 3 4 5 6 7 8 9 [dependencies] protobuf = "2" grpc = "0.8.3" grpc-protobuf = "0.8.3" futures = "0.3.*" tls-api = "0.9.*" [build-dependencies] protoc-rust-grpc = "0.8.3"
关于这个crate详细信息,参考 https://crates.io/crates/grpc
demo参考 https://github.com/stepancheg/grpc-rust/tree/master/grpc-examples/greeter
参考上面链接,在src同级目录下新建build.rs,并做相应修改,内容如下:
1 2 3 4 5 6 7 8 9 10 extern crate protoc_rust_grpc;fn main () { protoc_rust_grpc::Codegen::new () .out_dir ("src" ) .input ("foobar.proto" ) .rust_protobuf (true ) .run () .expect ("protoc-rust-grpc" ); }
在src/lib.rs中,
1 2 3 4 5 6 pub mod foobar;pub mod foobar_grpc;
https://learnku.com/articles/44387
https://learnku.com/users/53818
https://lvlv.fun/posts/2022-05-30/
https://jasonkayzk.github.io/2022/12/03/Rust%E7%9A%84GRPC%E5%AE%9E%E7%8E%B0Tonic/
https://rustmagazine.github.io/rust_magazine_2021/chapter_5/rust_grpc_load_balancing.html
原文链接: https://dashen.tech/2023/12/03/Rust使用gRPC/
版权声明: 转载请注明出处.