Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to run server by #[tokio::main] #1283

Closed
IThawk opened this issue Jan 17, 2020 · 40 comments
Closed

how to run server by #[tokio::main] #1283

IThawk opened this issue Jan 17, 2020 · 40 comments

Comments

@IThawk
Copy link

IThawk commented Jan 17, 2020

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(())
}

@fafhrd91
Copy link
Member

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

@IThawk
Copy link
Author

IThawk commented Jan 17, 2020

I do not want add actix_rt

@fafhrd91
Copy link
Member

It is integral part of the project.

@geauxvirtual
Copy link

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
    });
}

anacrolix added a commit to getlantern/replica that referenced this issue Jan 24, 2020
This is how it should look. But actix-web requires special initialization inside tokio. See actix/actix-web#1283.
@pythoneer
Copy link
Member

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.

@svenallers
Copy link
Contributor

@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)
}

@jnicholls
Copy link

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.

@fakeshadow
Copy link
Contributor

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.

@jnicholls
Copy link

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.

@fakeshadow
Copy link
Contributor

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

@jnicholls
Copy link

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.

@fakeshadow
Copy link
Contributor

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.

@esemeniuc
Copy link

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)
}

@sylvain101010
Copy link

sylvain101010 commented Dec 10, 2020

Hi @fakeshadow and @jnicholls Just to understand correctly as I'm in the same situation (wanting to run actix-web in my own tokio runtime):

actix-web uses 1 thread (on a single threaded tokio runtime) to accept and dispatch requests, x worker threads (not part of the tokio runtime) to process the requests and ACTIX_THREADPOOL for blocking operations?

@chaoky
Copy link

chaoky commented Jan 3, 2021

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)
}

what is sys.await?; doing here?

@Type1J
Copy link

Type1J commented Jan 24, 2021

@chaoky That's a good question. I'm not sure. At first glance, it looks like it's there to give sys a chance to return an Error, but the HttpServer would have already returned an Error, if one had happened. I tried removing the HttpServer, and letting the sys.await?; line execute, and I got "thread 'main' panicked at 'spawn_local called from outside of a task::LocalSet'", so if the HttpServer ever did resolve its Future, it would probably crash. I'd say that you could put it in a local.run_until(async {}), but if you did, it seems to wait forever.

@isubasinghe
Copy link

I wonder if anyone could give me a hand with this issue. I am using tokio, actix and actix-web. I got "thread 'main' panicked at 'spawn_local called from outside of a task::LocalSet'" :(. Does anyone have a clue about how to resolve this?

@robjtede
Copy link
Member

robjtede commented Jan 26, 2021

Possibly a mis-match of Tokio versions; current stable actix-web (v3.3) supports Tokio v0.2. Tokio v1 support coming soon.

@jdrouet
Copy link

jdrouet commented Jan 26, 2021

I've the same problem than @isubasinghe. Running actix-web@4.0.0-beta-1 and tokio@^1 and I'm getting the following

[2021-01-26T17:29:41Z INFO  codebench_server] starting server
[2021-01-26T17:29:41Z INFO  actix_server::builder] Starting 8 workers
[2021-01-26T17:29:41Z INFO  actix_server::builder] Starting "actix-web-service-127.0.0.1:4000" service on 127.0.0.1:4000
thread 'main' panicked at '`spawn_local` called from outside of a `task::LocalSet`', /home/jdrouet/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.0.2/src/task/local.rs:202:18

@robjtede
Copy link
Member

Need to see code to help further.

@fakeshadow
Copy link
Contributor

fakeshadow commented Jan 27, 2021

a task::LocalSet panic is most likely not related to tokio version. It's an error happen when you do exactly like the error tells: calling spawn_local outside the scope of a LocalSet.
You should always make sure you call tokio::spawn_local or actix_rt::spawn inside a LocalSet. See tokio's doc for some context here. https://docs.rs/tokio/1.1.0/tokio/task/struct.LocalSet.html

and in general don't tokio::main on actix-web when you can. actix-web has it's main macro for a very good reason.

@isubasinghe
Copy link

isubasinghe commented Jan 27, 2021

Yup its not related to tokio version. I used the same version used in the library itself.
Fix:
I just used two runtimes, its not the cleanest code, but im just coding for fun, so meh

@Overdash
Copy link

Overdash commented Mar 7, 2021

Yup its not related to tokio version. I used the same version used in the library itself.
Fix:
I just used two runtimes, its not the cleanest code, but im just coding for fun, so meh

Could you kindly share how you did this?

I'm trying to run actix_web in a tokio runtime because I have a lot of essential services which use tokio runtime (e.g. lapin for AMQP and Async redis). It's hassle wrapping their functions around a crudely-made tokio runtime, or bouncing a tokio runtime in the HttpServer as part of the data.

@jnicholls
Copy link

jnicholls commented Mar 7, 2021

@Overdash You can spin up a new thread with std::thread::spawn and start the actix runtime in it. The system Arbiter (single-threaded Tokio runtime) can run until completion in that thread; all the worker Arbiters are created for you automatically on their own threads, you do not have any great control over that. This will effectively be running actix on its own tokio runtimes, one per Arbiter. However, if you want any async work coming from an actix web request to run on your “main” tokio runtime, you can spawn the futures onto that main runtime. You’ll just have to configure the actix server with a Handle to that runtime, e.g. using the AppData configuration.

@Overdash
Copy link

Overdash commented Mar 7, 2021

@jnicholls I see - thanks. Stupid question: how would I run the actix-web server on it's own thread without actix_web::main ?(assuming I cannot use that with this approach)

@jnicholls
Copy link

jnicholls commented Mar 7, 2021

@Overdash No problem. There are no stupid questions! :) Procedural macros like main macros are a double-edged sword: they hide the verbose details out of convenience, at the expense of never seeing and understanding those verbose details (without some intentional inspection).

The actix_web::main macro is aliasing actix_rt::main which is defined here. You can see that it is a fairly simple macro. It transforms

#[actix_rt::main]
async fn main() {
    println!("Hello World!");
}

into this

fn main() {
    actix_rt::System::new().block_on(async move {
        println!("Hello World!");
    })
}

tokio::main works fairly similarly where it starts up a default Runtime and wraps the main body into a Future with async move { ... }. So in your new thread, you can start up a fresh System in the same way and run a Future to completion (like an actix_web HttpServer!). Hope this helps.

@thangchung
Copy link

@jnicholls I have seen that with new actix-web (4.0.0-beta.3), we have actix_web::rt::System::with_tokio_rt to run the tokio runtime. But I haven't found any example on how to use it. Could anyone give me an example to use actix-web with tokie?

@robjtede
Copy link
Member

robjtede commented Mar 23, 2021

@thangchung That method is unstable for the time being (it's doc hidden). If that's okay with you, check out this example: https://github.com/actix/actix-net/blob/master/actix-rt/examples/hyper.rs.

@fakeshadow
Copy link
Contributor

actix/actix-net#266 (comment)

This PR would hopfully finally close this.

@sdykae
Copy link

sdykae commented Jul 23, 2021

@fakeshadow any news :)?

@thangchung
Copy link

thangchung commented Jul 23, 2021

@iuslkae I have implemented a bit to work around at https://github.com/thangchung/actix-dapr-rs/blob/main/dapr-subscriber/src/main.rs#L106

@Overdash
Copy link

Overdash commented Jul 23, 2021

@fakeshadow any news :)?

Try out actix-web 4.0.0 beta channel. I use actix-web 4.0.0-beta.5 and can use tokio 1.x runtime without setting up an additional independent runtime

@sdykae
Copy link

sdykae commented Jul 23, 2021

@Overdash can you post an example 🥺

@Overdash
Copy link

@iuslkae pretty straight forward, use actix_web::main as normal and inside you can just use tokio dependent code. The actix_web::main acts like tokio::main when using actix-web v4

@pranaypratyush
Copy link

pranaypratyush commented Jul 25, 2021

I do the following. This creates 8 main-tokio threads and 8 actix-rt|system threads. Gives much better control over things.

use actix_web::{get, web, App, HttpServer, Responder};

#[get("/{id}/{name}/index.html")]
async fn index(path: web::Path<(u32, String)>) -> impl Responder {
    let (id, name) = path.into_inner();
    return format!("Hello {}! id:{}", name, id);
}

fn main() {
    actix_web::rt::System::with_tokio_rt(|| {
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .worker_threads(8)
            .thread_name("main-tokio")
            .build()
            .unwrap()
    })
    .block_on(async_main());
}

async fn async_main() {
    tokio::spawn(async move {
        println!("From main tokio thread");
        // Would panic if uncommented showing no system here
        // println!("{:?}",actix_web::rt::System::current());
    });

    HttpServer::new(|| {
        App::new()
            .wrap(actix_web::middleware::Logger::default())
            .service(index)
    })
    .workers(8)
    .bind("0.0.0.0:8088")
    .expect("Couldn't bind to port 8088")
    .run()
    .await
    .unwrap()
}

Cargo.toml ->

[dependencies]
tokio = {version = "1.9.0", features = ["full"]}
actix-web = "4.0.0-beta.8" 

@mrxiaozhuox
Copy link

I want to use tokio::task::spawn create a process to run the actix-web, is there some scheme can do it. 😃

@brandonros
Copy link

Any update on this?

@robjtede
Copy link
Member

Actix Web v4 works with #[tokio::main] out of the box.

@Overdash
Copy link

Actix-web 4.0 has direct support for #[tokio:main] check it out Here

@roshanjrajan
Copy link

Link to docs for an example - https://docs.rs/actix-web/latest/actix_web/rt/index.html#running-actix-web-using-tokiomain

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests