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

Create axum server example. #90

Open
trickster opened this issue Jul 22, 2022 · 6 comments
Open

Create axum server example. #90

trickster opened this issue Jul 22, 2022 · 6 comments
Labels
F-feature-request feature request

Comments

@trickster
Copy link

trickster commented Jul 22, 2022

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Create an example using axum::Server with custom runtime and method routers. Axum is a very good abstraction for servers. It has routers, extractors etc, which are convenient. However, when using it with monoio it is not trivial because of a bunch of reasons.

use axum::routing::*;
use axum::Router;
// use hyper::service::Service;
use hyper::{server::conn::Http, service::service_fn};
use monoio::net::TcpListener;
use monoio_compat::TcpStreamCompat;
use std::future::Future;
use std::net::SocketAddr;

#[derive(Clone)]
struct HyperExecutor;

impl<F> hyper::rt::Executor<F> for HyperExecutor
where
    F: Future + 'static,
    F::Output: 'static,
{
    fn execute(&self, fut: F) {
        monoio::spawn(fut);
    }
}


async fn root() -> &'static str {
    "hi there"
}

#[monoio::main(threads = 4)]
async fn main() {
    let mut app = Router::new()
        .route("/", get(root))
        .into_make_service_with_connect_info::<SocketAddr>();

    // let sv = app.as_service();

    println!("Running http server on 0.0.0.0:8000");
    let addr = ([0, 0, 0, 0], 8000);

    let listener = TcpListener::bind(addr.into()).unwrap();

    loop {
        let (stream, _) = listener.accept().await.unwrap();
        let stream = unsafe { TcpStreamCompat::new(stream) };
        monoio::spawn(
            Http::new()
                .with_executor(HyperExecutor)
                .serve_connection(stream, app), // error here, trait mismatch
        );
    }
}

I tried using make_service_fn from hyper, but it takes AddrStream instead of TcpStream, unfortunately AddrStream has no public constructor.

Describe the solution you'd like
A clear and concise description of what you want to happen.

In hyper_server example,

let _ = serve_http(([0, 0, 0, 0], 23300), hyper_handler).await;

we should be able to get

let app = Router::new().route("/", get(root)).into_make_service();
serve_http(..., app).await;

basically axum handlers.

Additional context
Add any other context or screenshots about the feature request here.

If we can't manage to get this done, we can talk to hyper/axum maintainers to allow for some abstractions for different kinds of executors.

@trickster trickster added the F-feature-request feature request label Jul 22, 2022
@ihciah
Copy link
Member

ihciah commented Jul 24, 2022

There is an example to use hyper with monoio. But hyper with custom runtime has some overhead(and the biggest problem is there may be some soundness issues since the diffrence of io interface: readiness based and completion based io).

So here the short answer is: Do not do that.

Currently we are developing a pure async http framework(link) from scrach. Now the h1 protocol is nearly finished, but it can not be used in production yet.

@trickster
Copy link
Author

@ihciah thanks for reply. I see tokio-uring advertising they are compatible with hyper and tonic though. Not really sure if they are compatible with total tokio stack (tokio, tower, hyper, axum)

@trickster
Copy link
Author

I am also investigating this library that is the best runtime agnostic http library I have ever come across, no runtime dependencies for trillium (but there are adapters for smol, async-std and tokio). This is not the fastest library out there (yet) tho, and also not very popular, but probably the easiest to use.

@ihciah
Copy link
Member

ihciah commented Jul 24, 2022

Tokio is compatiable with hyper and tonic but tokio-uring is not. The tokio's way is to support uring based on epoll, anything epoll supports will work(and maybe uring is or is not used). Here our way is to hide the driver and allow users write only one set of code.
The problem I mentioned(readiness based and completion based io) is not related to implemention detail. So what here we faces, other frameworks must face.
For HTTP framework, they can hardly to be runtime(at least io) independent. Hyper and axum depends on tokio::io, and trillium depends on async-io. These two io interfaces are all readiness based(normally they sense the io interests ready and do io syscall afterwards).

@trickster
Copy link
Author

trickster commented Jul 24, 2022

The library I linked only uses futures_lite::io which is runtime agnostic. But yes, there is Send + Sync everywhere like here which is not needed at all

@ihciah
Copy link
Member

ihciah commented Jul 24, 2022

Yes, all async-std related crates(including futures_lite, smol and async-io) use futures-io. It is readiness based.

Send and Sync is another thing. I think it is not the most improtant problem here.

Readiness based io can be canceled anytime, just not do syscall. But completion based io cannot(like io-uring, you submitted the fd and buffer pointer, you must wait for it. Otherwise if the buffer is dropped, the kernel may write to a wrong position). To solve the problem, tokio-uring and we choose to take the buffer ownership. So you can see there two styles of io interface is different and cannot be totally compatiable. It is impossible to use io_uring with readiness style io interface.

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

No branches or pull requests

2 participants