Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build multiple concurrent servers with Rust and Tokio?

I'm looking to build multiple concurrent servers on different ports with Rust and Tokio:

let mut core = Core::new().unwrap();
let handle = core.handle();

// I want to bind to multiple port here if it's possible with simple addresses
let addr = "127.0.0.1:80".parse().unwrap();
let addr2 = "127.0.0.1:443".parse().unwrap();

// Or here if there is a special function on the TcpListener
let sock = TcpListener::bind(&addr, &handle).unwrap();

// Or here if there is a special function on the sock
let server = sock.incoming().for_each(|(client_stream, remote_addr)| {
    // And then retrieve the current port in the callback
    println!("Receive connection on {}!", mysterious_function_to_retrieve_the_port);
    Ok(())
});

core.run(server).unwrap();

Is there an option with Tokio to listen to multiple ports or do I need to create a simple thread for each port and run Core::new() in each?

Thanks to rust-scoped-pool, I have:

let pool = Pool::new(2);

let mut listening_on = ["127.0.0.1:80", "127.0.0.1:443"];

pool.scoped(|scope| {
    for address in &mut listening_on {
        scope.execute(move ||{
            let mut core = Core::new().unwrap();
            let handle = core.handle();

            let addr = address.parse().unwrap();
            let sock = TcpListener::bind(&addr, &handle).unwrap();

            let server = sock.incoming().for_each(|(client_stream, remote_addr)| {
                println!("Receive connection on {}!", address);
                Ok(())
            });

            core.run(server).unwrap();
        });
    }
});

rust-scoped-pool is the only solution I have found to execute multiple threads and wait forever after spawning them. I think it's working but I was wondering if a simpler solution existed.

like image 805
Thibaud Dauce Avatar asked Mar 13 '17 09:03

Thibaud Dauce


2 Answers

You can run multiple servers from one thread. core.run(server).unwrap(); is just a convenience method and not the only/main way to do things.

Instead of running the single ForEach to completion, spawn each individually and then just keep the thread alive:

let mut core = Core::new().unwrap();
let handle = core.handle();

// I want to bind to multiple port here if it's possible with simple addresses
let addr = "127.0.0.1:80".parse().unwrap();
let addr2 = "127.0.0.1:443".parse().unwrap();

// Or here if there is a special function on the TcpListener
let sock = TcpListener::bind(&addr, &handle).unwrap();

// Or here if there is a special function on the sock
let server = sock.incoming().for_each(|(client_stream, remote_addr)| {
    // And then retrieve the current port in the callback
    println!("Receive connection on {}!", mysterious_function_to_retrieve_the_port);
    Ok(())
});

handle.spawn(sock);
handle.spawn(server);

loop {
    core.turn(None);
}
like image 183
46bit Avatar answered Sep 19 '22 02:09

46bit


I'd just like to follow up that there seems to be a slightly less manual way to do things than 46bit's answer (at least as of 2019).

let addr1 = "127.0.0.1:80".parse().unwrap();
let addr2 = "127.0.0.1:443".parse().unwrap();

let sock1 = TcpListener::bind(&addr1, &handle).unwrap();
let sock2 = TcpListener::bind(&addr2, &handle).unwrap();

let server1 = sock1.incoming().for_each(|_| Ok(()));
let server2 = sock2.incoming().for_each(|_| Ok(()));

let mut runtime = tokio::runtime::Runtime()::new().unwrap();

runtime.spawn(server1);
runtime.spawn(server2);

runtime.shutdown_on_idle().wait().unwrap();
like image 35
deontologician Avatar answered Sep 22 '22 02:09

deontologician