Actix Web独立线程运行时请求无响应,能否用线程实现跨线程请求?
Actix跨线程运行服务器并发送请求的问题
问题背景
我是Actix新手,想要实现在一个线程中启动服务器,另一个线程发送请求的逻辑。
初始代码
use actix_web::{web, App, HttpResponse, HttpServer}; use std::{sync::mpsc::channel, thread}; #[actix_web::main] async fn main() { let (tx, rx) = channel(); thread::spawn(move || { let srv = HttpServer::new(|| App::new().default_service(web::to(|| HttpResponse::NotFound()))) .bind("localhost:12347") .unwrap() .run(); let _ = tx.send(srv); }); reqwest::get("http://localhost:12347").await.unwrap(); let srv = rx.recv().unwrap(); srv.handle().stop(false).await; }
问题现象
代码可正常编译,但发送请求时会卡住,无法得到响应。
修改后的可行代码(使用Tokio任务)
根据建议改用Tokio异步任务实现后,问题解决:
use actix_web::{web, App, HttpResponse, HttpServer}; use std::{sync::mpsc::channel, thread}; #[actix_web::main] async fn main() { let (tx, rx) = channel(); tokio::spawn(async move { let srv = HttpServer::new(|| App::new().default_service(web::to(|| HttpResponse::NotFound()))) .bind("localhost:12347") .unwrap() .run(); let _ = tx.send(srv.handle()); srv.await.unwrap(); }); reqwest::get("http://localhost:12347").await.unwrap(); let handle = rx.recv().unwrap(); handle.stop(false).await; }
疑问
虽然用异步任务解决了问题,但我仍好奇:是否可以用普通线程实现该逻辑?毕竟同步函数内无法直接使用await。
解答
初始代码卡住的原因
Actix的HttpServer::run()返回的是一个Server类型的异步Future,而普通线程中没有异步Runtime来驱动这个Future执行。所以你只是创建了Server对象,但服务器并没有真正启动,导致请求无法被处理,最终卡住。
用普通线程实现的方案
要在普通线程中运行Actix服务器,需要手动在新线程内初始化Actix的异步Runtime,然后在Runtime中执行Server的Future。示例代码如下:
use actix_web::{web, App, HttpResponse, HttpServer}; use std::{sync::mpsc::channel, thread}; use actix_rt::Runtime; #[actix_web::main] async fn main() { let (tx, rx) = channel(); // 在普通线程中启动Actix Runtime并运行服务器 thread::spawn(move || { // 初始化Actix Runtime let rt = Runtime::new().unwrap(); let srv = HttpServer::new(|| App::new().default_service(web::to(|| HttpResponse::NotFound()))) .bind("localhost:12347") .unwrap() .run(); // 发送服务器句柄给主线程 let _ = tx.send(srv.handle()); // 在Runtime中await服务器运行的Future,阻塞线程直到服务器停止 rt.block_on(srv).unwrap(); }); // 等待服务器启动后发送请求 std::thread::sleep(std::time::Duration::from_millis(500)); reqwest::get("http://localhost:12347").await.unwrap(); // 获取服务器句柄并停止服务器 let handle = rx.recv().unwrap(); handle.stop(false).await; }
说明
- 普通线程中通过
actix_rt::Runtime::new()创建Actix的异步Runtime,然后用rt.block_on()来阻塞执行Server的Future,这样服务器才能真正启动并处理请求。 - 主线程发送请求前加了短暂延迟,是为了确保服务器已经完成启动并开始监听端口,避免请求过早发送导致失败(生产环境可以用更可靠的同步机制,比如通过mpsc等待服务器启动完成的信号)。
内容的提问来源于stack exchange,提问作者14159
相关产品推荐
相关产品推荐

