-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Non-blocking/Evented I/O #395
Comments
We agree. We're actively looking into it. Mio looks promising. We also need On Tue, Mar 24, 2015, 6:06 AM Jarred Nicholls [email protected]
|
Do you already have a vision how to embed mio into hyper? I'm very interested in async client and have enough time to contribute some code. |
I don't have a vision; I haven't looked that hard into how mio works. I'd love to hear suggestions. |
mio will be adding Windows support in the near future, so depending upon it should be a safe bet. The API surface of hyper's server will not have to change much if at all, but the client will need an async interface, either in the form of closure callbacks, a trait handler, something like a Future or Promise return value, etc. |
+1 for prioritizing a trait handler. Futures have some amount of overhead, and closure callbacks have even more overhead and can lead to callback hell. If the goal is maximum performance, an async handler interface would be a natural starting point. |
Yeah honestly a trait handler with monomorphization/static dispatch is the only way to go. |
+1 for the async handler Trait |
http://hermanradtke.com/2015/07/12/my-basic-understanding-of-mio-and-async-io.html awesome introduction to mio |
This is very much premature, but figured any activity to this thread is a positive! I have been playing around with what it would look like to write an asynchronous hyper client. here it goes: https://github.com/talevy/tengas. this has many things hardcoded, and is not "usable" by any means. Currently it does just enough I tried to re-use as many components of hyper as possible. Seems to work! I had to re-implement HttpStream to use I plan on making this more generic and slowly match the original hyper client capabilities. Any feedback is welcome! Code is a slight mess because it is the first pass at this to make it work. |
I've been investigating mio support, and fitting it in was actually pretty simple (in a branch). I may continue the branch and include the support with a cargo feature flag, but I can't switch over completely until Windows support exists. |
A feature flag makes great sense in this case then. There are plenty of On Sun, Jul 26, 2015 at 5:22 PM, Sean McArthur [email protected]
|
Servo would be super interested in hyper + mio to reduce the thread bloat :) |
hyper + mio looks very promising =) 👍 |
I would assume there would be some number of threads with event loops handling http requests rather than one thread with one event loop? |
@seanmonstar is this branch public somewhere? |
Not yet. It doesn't use an event loop yet, I simply switched out usage of On Mon, Jul 27, 2015, 8:56 AM Tal Levy [email protected] wrote:
|
If hyper can add that feature I'd basically consider it usable for myself in production, otherwise it would probably cause a great deal of thread context switching for my use case (lots and lots and lots of short lived connections) |
It is interesting how stable express, vanilla, and spray are in terms of On Thu, Aug 6, 2015 at 5:33 AM, Sergey Kamardin [email protected]
|
@jnicholls fair enough 🍻 |
I have a vision. In short it boils down to splitting hyper into three logical parts:
The first item is basically ok, except maybe put types into separate crates. But logic is too coupled with streams. Decoupling it should also simplify testing AFAIU. Then we can do competing experimental asychronous I/O implementations without rewriting too much of hyper. (I will publish my implementation soon). The biggest question on mio right now is how to make things composable. I.e. you can't mix multiple applications in same async loop, until some better abstractions are implemented, so I'm currently experimenting with it. How does this sound? What I need to start contributing these changes into hyper? |
I agree that hyper should decouple the logic of composing and parsing HTTP That's a lot of freedom offered by simply splitting up the composition and On Fri, Aug 7, 2015 at 8:14 PM, Paul Colomiets [email protected]
|
@Ogeon I believe I've not fixed this on the branch. |
Yeah, it's still there. Another thing I've noticed is that |
@Ogeon As I should have done, I've added a test in the server tests for that exact instance, and I can now say it is fixed. As for read errors, you mean errors printed by env_logger from hyper (that the socket closed?), or the read errors reported directly from wrk? The former, I've fixed (it was a useful error log when first working on the branch). The latter, I haven't seen in a long time. |
Ah, nice! I can confirm that it works now.
It's directly reported from
It looks like it's not one for each request, so I'm not really sure what would trigger them. Could it be something I have missed, when implementing it? |
Hm, read errors from wrk are reported each time 'read(fd)' returns -1. Does I don't see this in the hello world server example in hyper. What response On Thu, Mar 31, 2016, 3:54 AM Erik Hedvall [email protected] wrote:
|
It's the hello_world example from Rustful, and it's the same for other examples there, as well. It should just write "Hello, stranger!" and the headers should just be date, content length, content type, and server name. It looks fine in the browser, so that's why I'm a bit confused. I tried some of the Hyper examples and they didn't generate the errors, so there must be something with how I'm doing it. I'll investigate more later, and see what I can find. |
Ok, I found something. The "simplified" handler goes into wait mode if no body should be read, and waits for a signal that tells it that it's time to start the writing the response. Changing this to make it immediately go into write mode (which will mess with the flow, but should work for I tried to replicate the What do you think? |
@Ogeon hm, I am able to reproduce if I |
Hard to say. The error conditions seems to be either a network error (return -1), a 0 read before finishing the response, or some failure in If it does read something, then the problem should be caused by something in I tried to pass the |
It looks like they forgot to check errno in sock_read to return RETRY, and On Sat, Apr 2, 2016, 4:03 AM Erik Hedvall [email protected] wrote:
|
Hmm, yeah, looks like that's the case. It's also static, so I guess it won't be replaced by an other function. |
I've noticed that the latency, and variation of latency with the mio branch is much higher and seems to have a larger spread than whats currently on master. I ran cargo --release --example server in each branch then ran wrk against it. Mio
Master
Can anyone else confirm this? Is this a known issue? |
The mio branch cpu usage is like half of the threaded. That might be related |
It may help to edit the async example to spawn a server in several threads, so as to compare to the master example which is using 1.25 threads per cpus. |
I just noticed that the way |
@Ogeon It was indeed a bug. I added the use of an |
Alright, nice! 😄 It works just as before, now. |
I've been trying to re-enable OpenSSL support in Rustful today, and it went alright, except when it came to dealing with the different
It would be nice if the |
@Ogeon ah, I hadn't noticed that the |
That should do it, as far as I could tell. It did look like it was just |
Ignore me if you've already done this, but if you're using non-blocking sockets with OpenSSL you can't just use the If you don't, it will work most of the time and then occasionally fail. [1] http://sfackler.github.io/rust-openssl/doc/v0.7.9/openssl/ssl/struct.SslStream.html#method.ssl_read |
@erikjohnston yep, I know about it. I haven't handled it yet, as I slowed down trying to design a generic way for the Basically, my initial thoughts were something like this: trait Transport {
// ...
fn blocked(&self) -> Option<Blocked> {
// default implementations assume nothing special
None
}
}
enum Blocked {
Read,
Write,
} The So an implementation for openssl could do something like this: struct OpensslStream {
stream: openssl::SslStream<HttpStream>,
blocked: Option<Blocked>,
}
impl Read for OpensslStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.stream.ssl_read(buf) {
Ok(n) => Ok(n),
Err(openssl::Error::WantWrite(io)) => {
self.blocked = Some(Blocked::Write);
return Err(io);
},
// ...
}
}
} Does this seem like it would work for any other transport implementation, like schannel or security-framework? @sfackler @frewsxcv |
That should work, yeah, but it's unfortunate that there's that out of band signaling of what you're blocked on. Maybe there should be a tweaked Read trait that could return the proper information? |
@sfackler well, the stream is present to the |
@seanmonstar I could implement the nicer solution nr. 2 now, when |
Hyper would be far more powerful of a client & server if it was based on traditional event-oriented I/O, single-threaded or multi-threaded. You should look into https://github.com/carllerche/mio or a wrapper around libuv or something of that sort.
Another option is to split hyper up into multiple crates, and refactor the client & server to abstract the HTTP protocol handling (reading & writing requests/responses onto a Stream) so that someone can use the client and/or server logic on top of their own sockets/streams that are polled in an event loop. Think, libcurl's multi interface + libuv's uv_poll_t.
The text was updated successfully, but these errors were encountered: