2 ms·
The article looks great! I like that it explains both classic IO for a webserver (which helps since a lot of developers don't even know that read() and write()
by Matthias247 3y ago
The article looks great! I like that it explains both classic IO for a webserver (which helps since a lot of developers don't even know that read() and write() might return without all data being processed) and then proceeds from non-blocking IO to async/await.
However there seems one thing worthwhile to point out:
> All to implement graceful shutdown.
It isn't true that async IO is required for that. Blocking IO calls can also be interrupted via signals. For the use-case of Ctrl+C that is mentioned in the blog post even the simple setup which unblock the syscall and let it return EINTR are successful. But even custom in-application cancellation from different threads works via signals. E.g. building a Thread::interrupt() that unblocks IO on a different thread is possible.
Most classical applications (e.g. proxy-servers) that went for the nonblocking route did it indeed for bigger scalability and lower memory footprint than using a thread per connection model. In the last couple of years and especically in Rust I however feel that most applications choose async either because the library ecosystem forces them or because there's just an assumption of missing out on something if not using it. There's rarely measurements performed with both setups that actually quanitfy any win or loss of using async IO.
- ibraheemdev 3y agoYeah you're right regarding signal handlers (though I'm not sure the windows equivalent), and I was planning on adding a note about the alternative. The main thing I wanted to point out was that async/await as a model is built such that expressing complex control flow with arbitrary inputs becomes very simple. Nowadays thread-per-request will work well performance wise for most, but async/await became the default in various ecosystems because it's a more expressive model, especially useful in server-level code, which more or less forces it to be used by everyone. The advantage becomes apparent when you need to compose more complex logic within a system, which async makes seamless (though in Rust specifically things are harder because of the interaction between async and the borrow checker).