【问题标题】:Error "BlockingClientInFutureContext" when trying to make a request from within an actix-web route handler function尝试从 actix-web 路由处理程序函数中发出请求时出现错误“BlockingClientInFutureContext”
【发布时间】:2019-10-10 13:38:01
【问题描述】:

我正在使用 Rust 2018 Stable 和 Actix-Web 编写 Web 服务。使用 Reqwest,我正在从一个路由处理程序函数中向不同的站点发出 HTTP 请求。简单来说就是这个样子

extern crate reqwest;
use actix_web;
use reqwest::Url;

pub fn testing(req: actix_web::HttpRequest) -> actix_web::Result<actix_web::HttpResponse> {
    println!(">>> testing request begin");
    let url = Url::parse("https://example.com/").unwrap();
    println!(">>> testing url built");
    let req = reqwest::Client::new().post(url);
    println!(">>> testing req prepared");
    let res_struct = req.send();
    println!(">>> testing res_struct received");
    let res = res_struct.unwrap();
    println!(">>> testing res unwrapped");
    Ok(format!("done.").into())
}

这不起作用,我收到以下错误消息(错误打印了 8 次,“worker:1”到“worker:8”,尽管只调用了一次函数):

thread 'actix-rt:worker:1' panicked at 'called `Result::unwrap()` 
on an `Err` value: Error(BlockingClientInFutureContext, 
"https://www.example.com/")', src/libcore/result.rs:999:5
Panic in Arbiter thread, shutting down system.

Google 在 “BlockingClientInFutureContext” 上没有发现任何有用的信息,但我猜它与 async/await 或 Tokio 自己的未来有某种关系?

感谢您提供有关阅读内容的任何指示。另外,我是 Rust 新手。

处理函数从 Actix-Web HTttpServer 调用

HttpServer::new(|| App::new().service(
    web::resource("/testing").route(
        web::get().to(views::testing)
    )
)).bind("127.0.0.1:8001")?.run()

【问题讨论】:

  • 我认为 tokio 或 async/await 不会调用 'unwrap()'。在您的代码中:.post(Url::parse(API_ENDPOINT).unwrap()),您正在调用 unwrap(),如果 unwrap() 失败,这将导致恐慌。您是否尝试过将其更改为不使用 unwrap() 的调用?
  • 感谢您的想法,我更新了问题。展开 reqwest 响应时它会发生恐慌。
  • 我认为如果您可以提交带有主 fn 的 minimal reproducible example 会有所帮助,以便成员可以将您的示例复制并粘贴到他们的 IDE 中。
  • 我用一个完整的例子更新了这个问题。几乎可以肯定这可以通过actix_web::web::block() 以某种方式解决。

标签: rust actix-web reqwest


【解决方案1】:

我遇到了类似的问题。我的解决方案是在您的货物文件中将 Reqwest crate 版本锁定为 0.9.17,然后重新构建。

reqwest = "=0.9.17"

似乎较新版本的 Reqwest 已被 Actix-web 破坏,除非您在两者上都使用异步功能。供参考:https://github.com/seanmonstar/reqwest/issues/541

【讨论】:

    【解决方案2】:

    事实证明,actix_web::web::block() 是正确的猜测。使用它可以进行阻塞调用。 block() 返回一个 Future,一旦网络请求返回数据就会解析。非常接近 JS 中的 Promises,加上里面的 .from_err()

    pub fn testing(_req: actix_web::HttpRequest)
        -> impl Future<Item = HttpResponse, Error = Error>
    {
        println!(">>> testing request begin");
        let url = Url::parse("https://example.com/").unwrap();
        println!(">>> testing url built");
        let req = reqwest::Client::new().get(url);
        println!(">>> testing req prepared");
    
        actix_web::web::block(move || {
            println!(">>> testing res received");
            req.send()
        })
        .from_err()
        .and_then(|res| {
            println!(">>> testing res: {:?}", &res);
            HttpResponse::Ok().content_type("text/html").body("Hello!")
        })
    }
    

    此外,在main.rs 中,必须使用.to_async() 而不是简单的.to() 调用路由:

    HttpServer::new(|| App::new().service(
        web::resource("/testing").route(
            web::get().to_async(views::testing)
        )
    )).bind("127.0.0.1:8001")?.run()
    

    【讨论】:

    • 谢谢,我几乎为这个问题浪费了一天的时间,但是这个解决方案不适用于 std::future,是吗?因为future crate被拉了我不知道如何解决这个问题
    【解决方案3】:

    TL;DR:升级到 reqwest 0.9.22 或更高版本。

    该错误表明您正试图从异步上下文中进行阻塞网络调用(Actix Web 处理程序被异步调用)。 reqwest 版本 0.9.17 - 0.9.21 不支持此功能。

    截至 0.9.22,作者已删除此错误以支持警告。欲了解更多信息:

    https://github.com/seanmonstar/reqwest/pull/670

    https://github.com/seanmonstar/reqwest/issues/541

    【讨论】:

      【解决方案4】:

      unwrap() 的调用失败,因为返回了错误。最好在生产代码中避免使用unwrap(),因为这通常意味着我们试图查看“期望”值(通常称为“快乐路径”)而忽略错误路径。

      此代码有效:

      use actix_web;
      use reqwest::Url;
      
      fn main()  {
          println!(">>> testing request begin");
          let url = Url::parse("http:/example.com/").unwrap();
          println!(">>> testing url built");
          let req = reqwest::Client::new().post(url);
          println!(">>> testing req prepared");
          let res_struct = req.send();
          println!(">>> testing res_struct received");
          match res_struct {
              Ok(r)=> println!("response: {:?}", r),
              Err(e)=> println!("error: {}", e),
          }
      //    let res = res_struct.unwrap();
          println!("done.");
      }
      

      输出是:

      Finished dev [unoptimized + debuginfo] target(s) in 2.63s
           Running `target/debug/untitled`
      >>> testing request begin
      >>> testing url built
      >>> testing req prepared
      >>> testing res_struct received
      error: http://example.com/: error trying to connect: failed to lookup address information: nodename nor servname provided, or not known
      >>> testing res unwrapped
      done.
      

      上面的代码可以正常运行,但example.com 的服务器没有提供良好的响应。 如果我使用有效的 URL 重新运行它,例如https://cisco.com,我没有收到任何错误:

      >>> testing request begin
      >>> testing url built
      >>> testing req prepared
      >>> testing res_struct received
      response: Response { url: "https://www.cisco.com/", status: 200, headers: {"server": "Apache", "etag": "\"1732e-59058880c8465\"", "accept-ranges": "bytes", "strict-transport-security": "max-age=31536000", "cdchost": "wemxweb-publish-prod2-02", "x-xss-protection": "1; mode=block", "x-test-debug": "nURL=www.cisco.com,realm=0,isRealm=0,realmDomain=0,shortrealm=0", "content-security-policy": "upgrade-insecure-requests; frame-ancestors *.cisco.com *.jasper.com *.ciscospark.com *.ciscolive.com  http://cisco.lookbookhq.com https://cisco.lookbookhq.com testcisco.marketing.adobe.com cisco.marketing.adobe.com ciscosales.my.salesforce.com test.salesforce.com zedo.com hindustantimes.com economictimes.indiatimes.com *.webex.com *.cdw.com *.cdwg.com *.cdw.ca *.meraki-go.com http://ciscopartners.lookbookhq.com https://ciscopartners.lookbookhq.com ciscolearningsystem.com ciscocustomer.lookbookhq.com cisco.lookbookhq.com;", "content-type": "text/html", "expires": "Sun, 18 Aug 2019 12:10:23 GMT", "cache-control": "max-age=0, no-cache, no-store", "pragma": "no-cache", "date": "Sun, 18 Aug 2019 12:10:23 GMT", "connection": "keep-alive", "vary": "Accept-Encoding"} }
      >>> testing res unwrapped
      done.
      

      【讨论】:

      • 谢谢,但我仍然遇到同样的错误:error: https://cisco.com/: blocking Client used inside a Future context。这是在从 Actix-Web 路由器调用的请求处理程序中运行的,而不仅仅是从常规的 main() 函数调用。
      • 我猜你的minimal reproducible example 需要扩展一点:-)。
      • 我多次提到这是一个Actix-Web Request Handler函数。我什至在HttpServer 上发布了如何设置标准 Actix Web 路由器。我怎样才能让它更清楚?正是出于这个原因,该问题被标记为“Actix-Web”。
      猜你喜欢
      • 2020-12-07
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 2015-08-02
      相关资源
      最近更新 更多