问题
When creating a hyper post request inside an actix-web
resolver, the following error is thrown - how can one send one a http request by spawning the request into the existing executor?
thread 'actix-rt:worker:1' panicked at 'Multiple executors at once: EnterError { reason: "attempted to run an executor while another executor is already running" }', src/libcore/result.rs:999:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Panic in Arbiter thread, shutting down system.
main.rs
extern crate actix_web;
extern crate serde_json;
extern crate actix_rt;
extern crate hyper;
use serde_json::{Value, json};
use hyper::{Client, Uri, Body, Request};
use actix_web::{middleware, web, App, HttpResponse, HttpServer};
use actix_rt::System;
use actix_web::client;
use futures::future::{Future, lazy};
fn main() {
println!("Start server...");
listen();
}
pub fn listen() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.data(web::JsonConfig::default().limit(4096))
.service(web::resource("/push").route(web::post().to(index)))
.service(web::resource("/test").route(web::post().to(test)))
})
.bind("127.0.0.1:8080")?
.run()
}
fn index(item: web::Json<Value>) -> HttpResponse {
println!("model: {:?}", &item);
send(json!({
"hello": "world"
}));
HttpResponse::Ok().json(item.0) // <- send response
}
fn test(item: web::Json<Value>) -> HttpResponse {
println!("recevied test call!");
println!("{:?}", &item);
HttpResponse::Ok().json(item.0) // <- send response
}
pub fn send(mut data: serde_json::Value) {
println!("# Start running log post future...");
// if the following line is removed, the call is not received by the test function above
System::new("test").block_on(lazy(|| {
let req = Request::builder()
.method("POST")
.uri("http://localhost:8080/test")
.body(Body::from(data.to_string()))
.expect("request builder");
let client = Client::new();
let future = client.request(req)
.and_then(|res| {
println!("status: {}", res.status());
Ok(())
})
.map_err(|err| {
println!("error: {}", err);
});
return future;
}));
println!("# Finish running log post future")
}
cargo.toml
[package]
name = "rust-tokio-event-loop-madness"
version = "0.1.0"
authors = [""]
edition = "2018"
[dependencies]
serde_json = "1.0.39"
actix-web = "1.0.0"
serde_derive = "1.0.92"
actix-rt = "*"
hyper = "0.12.30"
futures = "*"
curl command to trigger error:
curl -X POST -H 'Content-Type: application/json' -d '{"test":1}' http://localhost:8080/push
Repo with example: https://github.com/fabifrank/rust-tokio-event-loop-madness
回答1:
Got it working by using the tokio
function spawn
to add the future to the running executor of tokio.
So instead of:
System::new("test").block_on(lazy(|| {
use:
spawn(lazy(move || {
and of course add tokio
as dependency in cargo.toml
and include the crate.
回答2:
This is because actix-web uses Tokio since version 1.0.0. As Reqwest does it as well, you end up with two runtimes.
One of the best way to handle this is to switch to the async version of both your handler and the reqwest request. The process can be a bit involved but is worth it in the long run. This article does a good job at explaining the transition.
来源:https://stackoverflow.com/questions/56634370/http-request-inside-actix-web-handler-multiple-executors-at-once-entererror