英文:
Running an actix web server on a separate thread
问题
以下是您提供的代码的中文翻译部分:
原始代码:
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;
}
修改后的代码:
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;
}
希望这有助于您理解代码和问题的解决方法。如果您有其他问题,可以随时提出。
英文:
I'm new to actix, and I'm trying to understand how I can run a server on one thread and send requests from another.
This is the code I have so far
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;
}
It compiles just fine, but it gets stuck on on sending the request. It seems like the server is running, soI can't figure out why I am not getting a response.
EDIT: As suggested by @Finomnis and @cafce25,I changed the code to use tasks instead of threads, and await
ed te result of .run()
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;
}
which solves the problem. I'm still curious if it is possible to do it on different threads since I can't use await
inside a synchronous function.
答案1
得分: 4
有几个问题你的代码; 其中最大的问题是你从未使用 .await
来执行 run()
方法。
因此,你不能在普通线程中运行它,它必须存在于异步任务中。
所以发生的情况是:
- 你创建了服务器
- 服务器从不运行,因为它没有被
await
- 你查询服务器以获取响应
- 响应永远不会到来,因为服务器没有运行,所以你卡在
reqwest::get
你应该做的是:
- 启动服务器。
另外:
- 你不需要传播服务器对象以停止它。在将其移入任务之前,你可以首先创建一个
.handle()
。服务器句柄不包含对服务器的引用,而是基于智能指针。 - 绝对不要在异步任务中使用同步通道。它将阻塞运行时,导致死锁。 (在第二个示例中它能工作的唯一原因是它很可能是多线程运行时,你只是死锁了一个运行时核心。仍然不好。)
- (也许)如果你使用
#[actix_web::main]
,就不要使用tokio::spawn
。actix-web
有自己的运行时,你需要使用actix_web::rt::spawn
。如果要使用基于tokio
的任务,需要使用#[tokio::main]
。actix-web
与 tokio 运行时兼容。 (EDIT: actix-web 可能 兼容tokio::spawn()
,我只是找不到任何说它是的文档)
修复了所有这些问题后,这是一个可工作的版本:
use actix_web::{rt, web, App, HttpResponse, HttpServer};
#[actix_web::main]
async fn main() {
let srv = HttpServer::new(|| App::new().default_service(web::to(|| HttpResponse::NotFound())))
.bind("localhost:12347")
.unwrap()
.run();
let srv_handle = srv.handle();
rt::spawn(srv);
let response = reqwest::get("http://localhost:12347").await.unwrap();
println!("Response code: {:?}", response.status());
srv_handle.stop(false).await;
}
英文:
There are a couple of things wrong with your code; the biggest one being that you never .await
the run()
method.
For that fact alone you cannot run it in a normal thread, it has to exist in an async task.
So what happens is:
- you create the server
- the server never runs because it doesn't get
await
ed - you query the server for a response
- the response never comes because the server doesn't run, so you get stuck in
reqwest::get
What you should do instead:
- start the server.
Also:
- You don't need to propagate the server object out to stop it. You can create a
.handle()
first before you move it into the task. The server handle does not contain a reference to the server, it's based on smart pointers instead. - NEVER use synchronous channels with async tasks. It will block the runtime, dead-locking everything. (The only reason it worked in your second example is because it is most likely a multi-threaded runtime and you only dead-locked one of the runtime cores. Still bad.)
- (Maybe) don't
tokio::spawn
if you use#[actix_web::main]
.actix-web
has its own runtime, you need toactix_web::rt::spawn
with it. If you want to usetokio
based tasks, you need to do#[tokio::main]
.actix-web
is compatible with the tokio runtime. (EDIT: actix-web might be compatible withtokio::spawn()
, I just didn't find documentation anywhere that says it is)
With all that fixed, here is a working version:
use actix_web::{rt, web, App, HttpResponse, HttpServer};
#[actix_web::main]
async fn main() {
let srv = HttpServer::new(|| App::new().default_service(web::to(|| HttpResponse::NotFound())))
.bind("localhost:12347")
.unwrap()
.run();
let srv_handle = srv.handle();
rt::spawn(srv);
let response = reqwest::get("http://localhost:12347").await.unwrap();
println!("Response code: {:?}", response.status());
srv_handle.stop(false).await;
}
Response code: 404
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论