Multi-threaded HttpListener with await async and Tasks

I've done something similar at https://github.com/JamesDunne/Aardwolf and have done some extensive testing on this.

See the code at https://github.com/JamesDunne/aardwolf/blob/master/Aardwolf/HttpAsyncHost.cs#L107 for the core event loop's implementation.

I find that using a Semaphore to control how many concurrent GetContextAsync requests are active is the best approach. Essentially, the main loop continues running until the semaphore blocks the thread due to the count being reached. Then there will be N concurrent "connection accepts" active. Each time a connection is accepted, the semaphore is released and a new request can take its place.

The semaphore's initial and max count values require some fine tuning, depending on the load you expect to receive. It's a delicate balancing act between the number of concurrent connections you expect vs. the average response times that your clients desire. Higher values mean more connections can be maintained yet at a much slower average response time; fewer connections will be rejected. Lower values mean less connections can be maintained yet at a much faster average response time; more connections will be rejected.

I've found, experimentally (on my hardware), that values around 128 allow the server to handle large amounts of concurrent connections (up to 1,024) at acceptable response times. Test using your own hardware and tune your parameters accordingly.

I've also found that a single instance of WCAT does not like to handle more than 1,024 connections itself. So if you're serious about load-testing, use multiple client machines with WCAT against your server and be sure to test over a fast network e.g. 10 GbE and that your OS's limits are not slowing you down. Be sure to test on Windows Server SKUs because the Desktop SKUs are limited by default.

Summary: How you write your connection accept loop is critical to the scalability of your server.


Technically you're right. To make it scalable you probably want to have multiple GetContextAsync running at the same time (performance testing needed to know exactly how many, but "a few for each core" is probably the right answer).

Then naturally, as pointed out by comments; not using IIS means you need to be pretty serious about security for a lot of things IIS gives you "for free".


I know I'm tremendously late to the party on this, but I published a library (source here https://github.com/jchristn/WatsonWebserver) on NuGet which encapsulates an async webserver.