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

Stable futures API #27

Merged
merged 30 commits into from
Jun 26, 2019
Merged

Stable futures API #27

merged 30 commits into from
Jun 26, 2019

Conversation

sagebind
Copy link
Owner

@sagebind sagebind commented May 14, 2019

Re-implement the core of chttp using std futures / futures 0.3 and async/await, and provide an API that can be used with async/await as well.

We may want to put this behind a compile-time feature somehow if possible...

  • Change Body to be async.
  • Implement Wakers for waking the agent thread.
  • Use an asynchronous pipe for streaming the response data from the agent thread to the main / request thread.
  • Fix anything that may have been broken during the refactor. (That's why we have integration tests!)
    • Fix redirect handling
    • Fix error handling

@sagebind sagebind force-pushed the next branch 2 times, most recently from c47ee1e to f8983a1 Compare May 24, 2019 02:56
@sagebind sagebind added this to the 0.5 milestone Jun 4, 2019
@ta3pks
Copy link
Contributor

ta3pks commented Jun 11, 2019

I just cannot wrap my head around this idea. Why would anyone want an always async http client? Most of the time waiting for the response actually makes sense. In general what is wrong with opening threads for async requests?

@sagebind
Copy link
Owner Author

It's primarily an efficiency thing. If you have a service that may need to make a request from more than one thread, or potentially parallel requests, you have three options:

  • Create a brand new connection for every request that operates totally independently. Advantages: simple to do. Disadvantages: you cannot take advantage of pipelining, HTTP/2+ advancements, or persistent connections to reduce latency.
  • Create a shared HTTP client that uses mutexes to protect concurrent access to a connection pool. This is the approach taken by several popular clients in other languages. Advantages: reduced latency by re-using connections. Disadvantages: parallel requests is limited by number of connections and threads in your thread pool.
  • Multiplex many connections at once using an event loop. This is the approach chttp uses (since 0.2). Advantages: reduced latency by re-using connections, low memory overhead by using only one extra thread for all requests, theoretically unlimited parallel requests. Disadvantages: most complex to implement.

I've benchmarked these approaches; chttp is actually 30% faster than using a vanilla curl request, because of its asynchronous design that takes advantage of curl's built-in (but optional) async features. It's more work for me to implement, but worth it in my book.

This particular PR will not make any major changes to the API; the default will be a synchronous API. If you want to make requests asynchronously (which a number of people do), then it will be offered also. In fact chttp's core is already asynchronous and powered by futures. The fact that no one notices this is a success in my book. It means I already successfully abstracted away the complexity involved.

All this PR does is change the guts from using futures version 0.2 to using std::future::Future (soon to be released) and also includes some additional optimizations to improve performance even more.

The nice thing about an asynchronous core is that it benefits both normal synchronous users and asynchronous users.

@sagebind
Copy link
Owner Author

Most of the time waiting for the response actually makes sense.

Indeed it does. But waiting does not always imply "blocking a thread". There are ways of waiting for something without blocking a thread. And that's valuable because threads are expensive and you want to use them as efficiently as possible.

In general what is wrong with opening threads for async requests?

I'm not sure exactly what you mean by this. Async and threads are not the same thing, if that's what you meant. In general, spawning new threads is really expensive, and you want to avoid doing it if you can. That's why for decades people have used things like thread pools or event loops, in order to conserve, control, and re-use threads efficiently without incurring a high cost.

@sagebind
Copy link
Owner Author

sagebind commented Jun 11, 2019

All this being said, I understand that this is a lot of complex talk, when there's a ton of users who don't care and really just want to make a few HTTP requests? Why does that have to be so difficult in Rust!? I sympathize, and that's exactly why I created chttp in the first place, and my vision has not changed. Making a GET request using chttp will never be harder than just chttp::get("http://example.org") if you don't want it to be.

My goal is to make the easy thing also the fast and efficient thing.

@ta3pks
Copy link
Contributor

ta3pks commented Jun 11, 2019

Thanks for you answers I guess I need to understand the futures deeper. Right now when people say async I remember the horribleness of writing javascript code and callbacks and stuff and to be honest that's really not how I want to write rust I just love being able to block when I need it. Again thanks for your detailed explanation and this amazing library. I guess I need to understand the futures and how to use them effectively first

@sagebind
Copy link
Owner Author

While futures and async/await in Rust seek to accomplish similar goals to promises and async/await in JavaScript, it is designed quite differently (and admittedly has a bit of essential complexity). Either way, you don't have to use futures in order to use chttp if you don't want to.

If you want to learn more about async in Rust, I'd wait until async/await syntax is stabilized in a month or so. It will be a lot easier then. I'm glad to help, and am glad if my HTTP client is useful to you.

@sagebind sagebind marked this pull request as ready for review June 18, 2019 05:16
@sagebind
Copy link
Owner Author

After more than a month of work, this PR is just about ready!

@sagebind sagebind merged commit f5f4acd into master Jun 26, 2019
@delete-merged-branch delete-merged-branch bot deleted the next branch June 26, 2019 02:34
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

Successfully merging this pull request may close these issues.

2 participants