使用Tokio实现嵌套并发时遇“cannot recursively call into `Core`”错误求解
解决Tokio Timer与Hyper结合时的"cannot recursively call into
Core" Panic 这个错误的核心原因是你在已经运行的Tokio Runtime中尝试创建并运行另一个Core实例——tokio::run本身已经启动了一个基于tokio-core的Runtime,而你在and_then回调里又调用了core.run(work).unwrap(),这就触发了递归调用Core的panic(Core的设计不允许嵌套运行)。
正确的做法:共享同一个Runtime Handle
你不需要在回调里新建Core,而是应该复用tokio::run提供的Runtime上下文,直接将HTTP请求的Future串联到Timer的Future链中,让整个任务在同一个Runtime里执行。
下面是修改后的完整代码:
修改后的main.rs
extern crate futures; extern crate hyper; extern crate tokio; extern crate tokio_core; extern crate tokio_timer; use futures::{Future, Stream}; use hyper::Client; use tokio_core::reactor::Handle; use std::time::{Duration, Instant}; use tokio::timer::Delay; use std::io::{self, Write}; fn main() { // 创建唯一的Core实例,所有异步任务都在这个Runtime中执行 let mut core = Core::new().unwrap(); let handle = core.handle(); let when = Instant::now() + Duration::from_secs(1); let task = Delay::new(when) .map_err(|e| panic!("timer failed; err={:?}", e)) .and_then(move |_| { // 使用已有的Runtime Handle创建Hyper Client let client = Client::new(&handle); let uri = "http://httpbin.org/ip".parse().unwrap(); // 将HTTP请求作为Future链的一部分返回,无需单独启动新的事件循环 client.get(uri).and_then(|res| { println!("Response: {}", res.status()); res.body() .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)) }) .map_err(|e| eprintln!("HTTP request failed: {:?}", e)) }); // 在同一个Core上运行整个任务链 core.run(task).unwrap(); }
为什么这样能解决问题?
- 我们只创建了一个
Core实例,所有异步任务(Timer和HTTP请求)都在这个Runtime中调度执行,彻底避免了递归调用Core的问题。 - Hyper的
Client依赖于Runtime的Handle,直接复用同一个Handle就能让HTTP请求在现有Runtime中运行,不需要额外启动新的事件循环。
额外优化:实现周期性任务
如果你需要定期重复发起HTTP请求(而不是只执行一次),可以把Timer和HTTP请求包装成循环的Future:
// 新增周期性任务函数 fn periodic_task(handle: Handle) -> impl Future<Item = (), Error = ()> { let when = Instant::now() + Duration::from_secs(1); Delay::new(when) .map_err(|e| panic!("timer failed; err={:?}", e)) .and_then(move |_| { let client = Client::new(&handle); let uri = "http://httpbin.org/ip".parse().unwrap(); client.get(uri).and_then(|res| { println!("Response: {}", res.status()); res.body() .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)) }) .map_err(|e| eprintln!("HTTP request failed: {:?}", e)) // 递归调用自身,实现周期性执行 .and_then(move || periodic_task(handle.clone())) }) } // 修改main中的任务执行逻辑 fn main() { let mut core = Core::new().unwrap(); let handle = core.handle(); let task = periodic_task(handle); core.run(task).unwrap(); }
这样你的服务就会每隔1秒发起一次HTTP请求,且不会触发之前的panic。
内容的提问来源于stack exchange,提问作者mchlstckl
相关产品推荐
相关产品推荐

