首页 > 解决方案 > 如何在并发请求之间共享 reqwest::Client?

问题描述

我无法让 reqwest 板条箱在重用同一个客户端时执行一堆异步请求。如果我不使用客户端而只使用提供的get界面,那么一切正常。本质上,我只是获取项目列表,然后使用异步获取列表中的所有内容。

但是,当我将客户端传递给我的get_object闭包时,编译器会抱怨该引用需要静态生命周期,并且比“客户端”还要长。

我如何成功地注释这一生?还是有更好的方法来完成我想要的?

extern crate futures; // 0.3.1
extern crate reqwest; // 0.10.1
extern crate serde; // 1.0.104
extern crate serde_json; // 1.0.45
extern crate tokio; // 0.2.10 

use serde::Deserialize;
use tokio::task;

#[derive(Debug, Deserialize)]
struct Item {
    name: Option<String>,
    id: Option<u64>,
}

pub trait ApiObject: Send {
    type Uri;

    fn from_json(text: &str) -> Result<Self, &'static str>
    where
        Self: Sized;

    fn url(uri: Self::Uri) -> String;
}

impl ApiObject for Item {
    type Uri = u64;

    fn from_json(text: &str) -> Result<Item, &'static str> {
        match serde_json::from_str::<Item>(text) {
            Result::Ok(val) => Ok(val),
            Result::Err(err) => match err.classify() {
                serde_json::error::Category::Data => Err("json input semantically incorrect"),
                serde_json::error::Category::Io => Err("failure to read/write bytes to io stream"),
                serde_json::error::Category::Syntax => Err("json input not syntactically valid"),
                serde_json::error::Category::Eof => Err("json data ended unexpectedly"),
            },
        }
    }

    fn url(uri: Self::Uri) -> String {
        format!("http://cats.com/items/{}.json", uri.to_string())
    }
}

/// Access point for any object inside the hybrid api
pub async fn get_object<O: 'static + ApiObject>(
    uri: O::Uri,
    client: &reqwest::Client,
) -> Result<O, &'static str> {
    let url = O::url(uri);

    let res = client.get(&url).send().await.unwrap();
    match res.status() {
        reqwest::StatusCode::OK => {
            let text = res.text().await.unwrap();
            task::spawn_blocking(move || to_struct(&text))
                .await
                .unwrap()
        }
        _ => Err("Request failed"),
    }
}

#[tokio::main]
async fn main() {
    let client = reqwest::Client::builder().build().unwrap();

    let top: Vec<u64> = vec![1, 2, 3, 4, 5];

    let futures: Vec<_> = top
        .iter()
        .map(|uri| get_object::<Item>(*uri, &client))
        .collect();

    let handles: Vec<_> = futures.into_iter().map(tokio::task::spawn).collect();

    let results = futures::future::join_all(handles).await;

    for result in results {
        let x = result.unwrap().unwrap();
        println!("{:#?}", x.name.unwrap());
    }
}

fn to_struct<T: ApiObject>(text: &str) -> Result<T, &'static str> {
    T::from_json(text)
}

操场

错误信息:

error[E0597]: `client` does not live long enough
  --> src/main.rs:73:46
   |
73 |         .map(|uri| get_object::<Item>(*uri, &client))
   |              ----- --------------------------^^^^^^-
   |              |     |                         |
   |              |     |                         borrowed value does not live long enough
   |              |     returning this value requires that `client` is borrowed for `'static`
   |              value captured here
...
84 | }
   | - `client` dropped here while still borrowed

它不同于如何使用 reqwest 执行并行异步 HTTP GET 请求?因为我使用闭包来传递客户端——使用async move块似乎对我没有帮助

标签: rustreqwest

解决方案


推荐阅读