我正在try 使用Rust的Tokio和reqwest crate 重新实现以下Python代码

#!/usr/bin/env python3

import asyncio
import httpx
import time

async def async_req(url, client):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54",
        "Cookie": ""
    }
    response = await client.get(url, headers=headers, timeout=180)
    print(response.status_code)
    print(response.text)

async def main():
    urls = ["http://localhost:5000/hello", "http://localhost:5000/world"]
    start = time.time()
    async with httpx.AsyncClient() as client:
        tasks = [async_req(url, client) for url in urls]
        await asyncio.gather(*tasks)
    print(time.time()-start)

if __name__ == "__main__":
    asyncio.run(main())

在Rust中实现的代码如下:

async fn async_req(
    url: &str,
    client: reqwest::Client,
) -> Result<reqwest::Response, Box<dyn std::error::Error>> {
    let response = client
        .get(url)
        .timeout(std::time::Duration::from_secs(180))
        .send()
        .await?;

    Ok(response)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let urls = vec![
        "http://localhost:5000/hello",
        "http://localhost:5000/world",
    ];

    let mut handles = vec![];

    let client = reqwest::Client::new();
    for url in urls {
        let handle = {
            let client = client.clone();
            async move { async_req(url, client).await }
        };
        handles.push(handle);
    }

    let mut responses = vec![];
    for handle in handles {
        responses.push(handle.await?);
    }

    for response in responses {
        println!("{}", response.text().await?);
    }

    Ok(())
}

但是,该程序似乎没有像在Python中那样发出异步请求.

我已经try 使用ChatGPT、Google和Stack Overflow来寻找解决方案,但我还没有找到不产生错误的解决方案.Cargo.toml的配置如下,我使用的Rust编译器版本是1.72.1(d5c2e9c34,2023-09-13)

[package]
name = "reqwest_async_demo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest = { version = "^0.11" }
tokio = { version = "^1", features = [ "full" ] }

如果有人能帮我澄清这个困惑,我将不胜感激,并欢迎任何回复.谢谢.

推荐答案

您可以使用tokio::try_join!futures::future::join_all同时运行期货.这些方法返回一个在所有期货都已完成时完成的future ,并生成其所有结果的集合.这类似于在Python中asyncio.gather的行为.

首先,将futures个 crate 添加到您的Cargo.toml:

[dependencies]
futures = "0.3"

主要功能:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let urls = vec![
        "http://localhost:5000/hello",
        "http://localhost:5000/world",
    ];

    let client = reqwest::Client::new();
    let mut handles = Vec::new();

    for url in urls {
        let client = client.clone();
        let handle = tokio::spawn(async move { async_req(url, client).await });
        handles.push(handle);
    }

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

    for response in responses {
        match response {
            Ok(Ok(res)) => println!("{}", res.text().await?),
            Ok(Err(e)) => eprintln!("Error: {}", e),
            Err(e) => eprintln!("Error: {}", e),
        }
    }

    Ok(())
}

tokio::spawn用于在新任务中并发地开始每个future ,然后使用futures::future::join_all来等待所有任务.join_all函数返回一个期货,它解析传递给它的期货结果的Vec.

另请注意,版本0.11中不存在reqwest中的timeout方法.您可以通过将请求包装在tokio::time::timeout call中来达到相同的效果.

async fn async_req(
    url: &str,
    client: reqwest::Client,
) -> Result<reqwest::Response, reqwest::Error> {
    let response = client
        .get(url)
        .timeout(std::time::Duration::from_secs(180))
        .send()
        .await?;

    Ok(response)
}

Rust相关问答推荐

rust 蚀将动力特性浇到混凝土 struct 上是行不通的

包含嵌套 struct 的CSV

什么样的 struct 可以避免使用RefCell?

如何从接收&;self的方法克隆RC

如何在Rust中表示仅具有特定大小的数组

integer cast as pointer是什么意思

在决定使用std::Sync::Mutex还是使用Tokio::Sync::Mutex时,操作系统线程调度是考虑因素吗?

铁 rust 中的泛型:不能将`<;T作为添加>;::Output`除以`{Float}`

如果死 struct 实现了/派生了一些特征,为什么Rust会停止检测它们?

在为第三方 struct 实现第三方特征时避免包装器的任何方法

如何将带有嵌套borrow /NLL 的 Rust 代码提取到函数中

如何处理闭包中的生命周期以及作为参数和返回类型的闭包?

一次不能多次borrow *obj作为可变对象

如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

Rust中如何实现一个与Sized相反的负特性(Unsized)

实现AsyncWrite到hyper Sender时发生生命周期错误

有没有办法通过命令获取 Rust crate 的可安装版本列表?

rust 中不同类型的工厂函数

意外的正则表达式模式匹配

TinyVec 如何与 Vec 大小相同?