尝试从 actix-web 路由处理程序函数中发出请求时出错 "BlockingClientInFutureContext"
Error "BlockingClientInFutureContext" when trying to make a request from within an actix-web route handler function
我正在使用 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 HTTPServer 调用处理函数 :
HttpServer::new(|| App::new().service(
web::resource("/testing").route(
web::get().to(views::testing)
)
)).bind("127.0.0.1:8001")?.run()
调用 unwrap()
失败,因为返回错误。最好避免在生产代码中使用 unwrap()
,因为这通常意味着我们在忽略错误路径的同时试图查看 'desired' 值(通常称为 'happy path')。
此代码有效:
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.
事实证明,actix_web::web::block()
是正确的猜测。使用它可以进行阻塞调用。 block()
returns 一个在网络请求 returns 数据后解析的 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()
我遇到了类似的问题。我的解决方案是在你的 cargo 文件中将 Reqwest crate 版本锁定在 0.9.17,然后重建。
reqwest = "=0.9.17"
看来较新版本的 Reqwest 与 Actix-web 不兼容,除非您在两者上都使用异步功能。供参考:https://github.com/seanmonstar/reqwest/issues/541
TL;DR: 升级到 reqwest 0.9.22 或更新版本。
该错误表明您正在尝试从异步上下文中进行阻塞网络调用(Actix Web 处理程序被异步调用)。这在 reqwest 版本 0.9.17 - 0.9.21 中不受支持。
从 0.9.22 开始,作者已删除此错误以提供警告。更多信息:
我正在使用 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 HTTPServer 调用处理函数 :
HttpServer::new(|| App::new().service(
web::resource("/testing").route(
web::get().to(views::testing)
)
)).bind("127.0.0.1:8001")?.run()
调用 unwrap()
失败,因为返回错误。最好避免在生产代码中使用 unwrap()
,因为这通常意味着我们在忽略错误路径的同时试图查看 'desired' 值(通常称为 'happy path')。
此代码有效:
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.
事实证明,actix_web::web::block()
是正确的猜测。使用它可以进行阻塞调用。 block()
returns 一个在网络请求 returns 数据后解析的 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()
我遇到了类似的问题。我的解决方案是在你的 cargo 文件中将 Reqwest crate 版本锁定在 0.9.17,然后重建。
reqwest = "=0.9.17"
看来较新版本的 Reqwest 与 Actix-web 不兼容,除非您在两者上都使用异步功能。供参考:https://github.com/seanmonstar/reqwest/issues/541
TL;DR: 升级到 reqwest 0.9.22 或更新版本。
该错误表明您正在尝试从异步上下文中进行阻塞网络调用(Actix Web 处理程序被异步调用)。这在 reqwest 版本 0.9.17 - 0.9.21 中不受支持。
从 0.9.22 开始,作者已删除此错误以提供警告。更多信息: