Actix-web: how to run server by #[tokio::main]

Created on 17 Jan 2020  路  13Comments  路  Source: actix/actix-web

use actix_web::{middleware, web, App, HttpRequest, HttpServer};

async fn index(_req: HttpRequest) -> &'static str {
"Hello world!"
}

[tokio::main]

[cfg(unix)]

async fn main() -> std::io::Result<()> {
::std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();

HttpServer::new(|| {
    App::new()
        // enable logger - always register actix-web Logger middleware last
        .wrap(middleware::Logger::default())
        .service(
            web::resource("/index.html")
                .route(web::get().to(|| async { "Hello world!" })),
        )
        .service(web::resource("/").to(index))
})
.bind_uds("/tmp/actix-uds.socket")?
.run()
.await

}

[cfg(not(unix))]

fn main() -> std::io::Result<()> {
println!("not supported");
Ok(())
}

Most helpful comment

@geauxvirtual Thx for your reply. That really helped me a lot. Just one minor addition. Maybe sth. changed in between but using #[tokio::main] with async fn main() works for me:

async fn hello_world() -> impl Responder {
    "Hello World!"
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let local = tokio::task::LocalSet::new();
    let sys = actix_rt::System::run_in_tokio("server", &local);
    let server_res = HttpServer::new(|| App::new().route("/", web::get().to(hello_world)))
        .bind("0.0.0.0:8000")?
        .run()
        .await?;
    sys.await?;
    Ok(server_res)
}

All 13 comments

You can not do this directly. You have to create actix_rt::System

I do not want add actix_rt

It is integral part of the project.

While I don't know what will happen with this project, though I do hope it continues in some fashion in the open source world, there is a way to run actix-web directly in a Tokio runtime. It does still require actix-rt, and it's not that straight forward to setup.

You can't use #[tokio::main] or async fn main().

But you can do the following:

fn main() {
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    let local = tokio::task::LocalSet::new();
    local.block_on(&mut rt, async move {
        tokio::task::spawn_local(async move {
            let local = tokio::task::LocalSet::new();
            let sys = actix_rt::System::run_in_tokio("server", &local);
            // define your actix-web app
            // define your actix-web server
            sys.await;
        });
        // This still allows use of tokio::spawn
    });
}

I think the question is answered and there is not much space to further the conversation. If there is a need to continue, feel free to do so and ask to reopen. Feature requests in the spirit of the conversation should be done in a separate issue.

Closed.

@geauxvirtual Thx for your reply. That really helped me a lot. Just one minor addition. Maybe sth. changed in between but using #[tokio::main] with async fn main() works for me:

async fn hello_world() -> impl Responder {
    "Hello World!"
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let local = tokio::task::LocalSet::new();
    let sys = actix_rt::System::run_in_tokio("server", &local);
    let server_res = HttpServer::new(|| App::new().route("/", web::get().to(hello_world)))
        .bind("0.0.0.0:8000")?
        .run()
        .await?;
    sys.await?;
    Ok(server_res)
}

I am struggling to get this run_in_tokio situation to work as I expect. My use case is I have an existing tokio runtime, and I want to run an actix-web server/runtime on that existing tokio multi-threaded runtime rather than having actix System spin up its own separate runtime. I'm having difficulty doing this with LocalSet due to the Server future not being Send. At the same time, I am trying to hold on to the Server reference in order to call stop to gracefully shut it down at any point in time.

So far it is not clear how to arrange this. Basic requirements are this:

  • Start actix-web HttpServer within existing Tokio runtime.
  • Hold copy of the Server that is returned from the HttpServer::run method, in order to call Server::stop on it at a later time.
  • I will be doing this from within a Tokio runtime, i.e. on a Tokio runtime thread. So tokio::spawn and Runtime::current are available tools.

Appreciate any thoughts on how to accomplish this. I do not understand why this was implemented using a LocalSet but hoping someone can shed some light. Thanks in advance.

There is no point of doing that. actix-web would spin up it's own threads regardless. You only start actix-web in a tokio runtime.

There is no point of doing that. actix-web would spin up it's own threads regardless. You only start actix-web in a tokio runtime.

Ok thanks. I assumed that the AsyncSystemRunner would utilize the existing Tokio runtime threads by running Arbiters on each one as necessary. No idea where I got that assumption; maybe it was just hope :) If the System is going to start threads regardless, I don't see the point of having the run_in_tokio method at all.

Thanks for your input.

The reason is actix-web uses !Send futures. So it's impossible to spawn any futures in a threaded runtime.

So to be clear:

  • actix-web spawns new threads to handle HTTP requests. Ok.
  • actix-web cannot inherit a Tokio runtime, it will always create its own.

Is that accurate? I care more about inheriting an existing Tokio runtime than I do about "extra threads." If you're saying that actix-web is going to run its own Tokio runtime anyways, then I'll just use System::new() and continue to wonder why run_in_tokio even exists. That is, I do not understand its use case give the above assertions.

actix-web would spawn a thread for listening sockets and distribute incoming streams to workers that all runs in it's own tokio current-thread runtime.
It can inherit tokio current-thread runtime which is what run_in_tokio does.
Use system::new is good practice and I don't get that API either.

To work off of @svenallers great post, one can use actix_web::rt rather than actix_rt:

async fn hello_world() -impl Responder {
    "Hello World!"
}

#[tokio::main]
async fn main() -std::io::Result<(){
    let local = tokio::task::LocalSet::new();
    let sys = actix_web::rt::System::run_in_tokio("server", &local);
    let server_res = HttpServer::new(|| App::new().route("/", web::get().to(hello_world)))
        .bind("0.0.0.0:8000")?
        .run()
        .await?;
    sys.await?;
    Ok(server_res)
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

kocoten1992 picture kocoten1992  路  3Comments

Dadibom picture Dadibom  路  4Comments

cheolgyu picture cheolgyu  路  3Comments

fafhrd91 picture fafhrd91  路  5Comments

naturallymitchell picture naturallymitchell  路  4Comments