Rust 如何使用reqwest执行并行异步HTTP GET请求?

Rust 如何使用reqwest执行并行异步HTTP GET请求?,rust,rust-tokio,reqwest,Rust,Rust Tokio,Reqwest,是有用的,但对于Rust和Tokio来说是新手,我正在努力解决如何一次处理N个请求,使用来自向量的URL,并为每个URL创建一个作为字符串的响应HTML迭代器 如何做到这一点?并发请求 从0.10开始: use futures::{stream,streamxt};//0.3.5 使用reqwest::Client;//0.10.6 使用tokio;//0.2.21,功能=[“宏”] const并发请求:usize=2; #[tokio::main] 异步fn main(){ 让client=c

是有用的,但对于Rust和Tokio来说是新手,我正在努力解决如何一次处理N个请求,使用来自向量的URL,并为每个URL创建一个作为字符串的响应HTML迭代器

如何做到这一点?

并发请求 从0.10开始:

use futures::{stream,streamxt};//0.3.5
使用reqwest::Client;//0.10.6
使用tokio;//0.2.21,功能=[“宏”]
const并发请求:usize=2;
#[tokio::main]
异步fn main(){
让client=client::new();
让URL=vec![”https://api.ipify.org"; 2];
let body=stream::iter(URL)
.map(| url |{
让客户端=&client;
异步移动{
让resp=client.get(url.send().await?;
resp.bytes()等待
}
})
.buffer_无序(并发_请求);
身体
.for|u each(| b | async{
比赛b{
Ok(b)=>println!(“获得{}字节”,b.len()),
Err(e)=>eprintln!(“出现错误:{}”,e),
}
})
.等待;
}

获取字符串的集合并将其转换为字符串

对流中的每个元素运行异步函数,并将元素转换为新类型

明确引用
客户端
,并将引用(而不是原始的
客户端
)移动到匿名异步块中

使用
客户端的连接池启动异步GET请求,并等待请求

请求并等待响应的字节数

将期货流转换为期货价值流,同时执行期货

将流转换回单个未来,打印出沿途接收的数据量,然后等待未来完成

另见:

无限制执行 如果愿意,还可以将迭代器转换为未来迭代器并使用:

我鼓励您使用第一个示例,因为您通常希望限制并发性,这将
buffer
buffer\u无序
帮助

并行请求 并发请求通常已经足够好了,但有时您需要并行请求。在这种情况下,您需要生成一个任务

use futures::{stream, StreamExt}; // 0.3.8
use reqwest::Client; // 0.10.9
use tokio; // 0.2.24, features = ["macros"]

const PARALLEL_REQUESTS: usize = 2;

#[tokio::main]
async fn main() {
    let urls = vec!["https://api.ipify.org"; 2];

    let client = Client::new();

    let bodies = stream::iter(urls)
        .map(|url| {
            let client = client.clone();
            tokio::spawn(async move {
                let resp = client.get(url).send().await?;
                resp.bytes().await
            })
        })
        .buffer_unordered(PARALLEL_REQUESTS);

    bodies
        .for_each(|b| async {
            match b {
                Ok(Ok(b)) => println!("Got {} bytes", b.len()),
                Ok(Err(e)) => eprintln!("Got a reqwest::Error: {}", e),
                Err(e) => eprintln!("Got a tokio::JoinError: {}", e),
            }
        })
        .await;
}
主要区别是:

  • 我们通常在单独的任务中执行工作
  • 我们必须为每个任务分配自己的
    reqwest::Client
    。作为示例,我们克隆了一个共享客户端以利用连接池
  • 当无法加入任务时,还有一个额外的错误情况
另见:


为什么要使用'stream::iter_ok(url).map(..)`?传递给
.map()
的闭包是否返回未来?我们应该使用
和_then
?或者,何时使用
.map()
以及何时使用
和_then()
。。两者都是流的吗?关于未来?@Nawaz请参阅文档:此流的项目是否可以转换为未来。另见,谢谢。我读过。我明白。但是缺少
.map()
部分。。另外,这如何转化为
(而不是
未来
)。在
的情况下,
和_然后对流中的每个成功项调用
。@AlexMoore Niemi足够公平了。更新。
.map(|url| {
let client = &client;
async move {
let resp = client.get(url).send().await?;
resp.bytes().await
.buffer_unordered(N);
bodies
    .for_each(|b| {
        async {
            match b {
                Ok(b) => println!("Got {} bytes", b.len()),
                Err(e) => eprintln!("Got an error: {}", e),
            }
        }
    })
    .await;
use futures::future; // 0.3.4
use reqwest::Client; // 0.10.1
use tokio; // 0.2.11

#[tokio::main]
async fn main() {
    let client = Client::new();

    let urls = vec!["https://api.ipify.org"; 2];

    let bodies = future::join_all(urls.into_iter().map(|url| {
        let client = &client;
        async move {
            let resp = client.get(url).send().await?;
            resp.bytes().await
        }
    }))
    .await;

    for b in bodies {
        match b {
            Ok(b) => println!("Got {} bytes", b.len()),
            Err(e) => eprintln!("Got an error: {}", e),
        }
    }
}
use futures::{stream, StreamExt}; // 0.3.8
use reqwest::Client; // 0.10.9
use tokio; // 0.2.24, features = ["macros"]

const PARALLEL_REQUESTS: usize = 2;

#[tokio::main]
async fn main() {
    let urls = vec!["https://api.ipify.org"; 2];

    let client = Client::new();

    let bodies = stream::iter(urls)
        .map(|url| {
            let client = client.clone();
            tokio::spawn(async move {
                let resp = client.get(url).send().await?;
                resp.bytes().await
            })
        })
        .buffer_unordered(PARALLEL_REQUESTS);

    bodies
        .for_each(|b| async {
            match b {
                Ok(Ok(b)) => println!("Got {} bytes", b.len()),
                Ok(Err(e)) => eprintln!("Got a reqwest::Error: {}", e),
                Err(e) => eprintln!("Got a tokio::JoinError: {}", e),
            }
        })
        .await;
}