为什么我不能使用 Rust tonic 并行发送多个请求?

Why can't I send multiple requests in parallel using rust tonic?

我实施了补品 helloworld 教程。然后我尝试更改客户端代码,以便在等待任何请求之前发送多个请求。

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let num_requests = 10;
    let mut client = GreeterClient::connect("http://[::1]:50051").await?;

    let mut responses = Vec::with_capacity(num_requests);
    for _ in 0..num_requests {
        let request = tonic::Request::new(HelloRequest {
            name: "Tonic".into(),
        });

        responses.push(client.say_hello(request));
    }
    for resp in responses {
        assert!(resp.await.is_ok());
    }

    Ok(())
}

这会导致编译错误:

error[E0499]: cannot borrow `client` as mutable more than once at a time
  --> src/client.rs:19:24
   |
19 |         responses.push(client.say_hello(request));
   |                        ^^^^^^ mutable borrow starts here in previous iteration of loop

这是否意味着 'client.say_hello()' returns 仍然引用客户端的类型,因此我无法再次调用 'say_hello',它本身需要“&mut self”?有没有办法在调用'await'之前继续发出请求?

来自Tonic documentation

Sending a request on a channel requires a &mut self and thus can only send one request in flight. This is intentional and is required to follow the Service contract from the tower library which this channel implementation is built on top of.
...
To work around this and to ease the use of the channel, Channel provides a Clone implementation that is cheap. This is because at the very top level the channel is backed by a tower_buffer::Buffer which runs the connection in a background task and provides a mpsc channel interface. Due to this cloning the Channel type is cheap and encouraged.

因此,您可以为您发出的每个并发请求克隆客户端。这消除了单个客户端在任何给定时间被多次借用的可能性,因此借用检查器得到安抚。

let num_requests = 10;
let client = GreeterClient::connect("http://[::1]:50051").await?;

let mut responses = Vec::with_capacity(num_requests);

for _ in 0..num_requests {
    let mut client = client.clone();

    let request = tonic::Request::new(HelloRequest {
        name: "Tonic".into(),
    });

    responses.push(tokio::spawn(async move {
        client.say_hello(request).await
    }));
}

for resp in responses {
    let resp = resp.await;
    assert!(resp.is_ok());
    assert!(resp.unwrap().is_ok());
}