Trimming the Fat Off of HTTP Endpoints

If you wanted to launch a REST based API today, what technology would you use? For most the answer would be Web API. Have you ever thought about what Web API consists of though? I mean, do you know how much code a request has to go through until it reaches your controller method?

While designing an API recently I forwent Web API and tried to get as low-level as possible in hopes that my service would be faster. I read an interesting tutorial on how this could be done in Azure using OWIN self hosting capabilities, but, for no good reason I am not a fan of OWIN. I get the sense that there is still a lot of other people’s code between the incoming request and my code. In my quest to get as low-level as possible I stumbled upon the HttpListener class which is essentially a wrapper for HTTP.sys. Surely this is as low-level as I can get without getting too carried away.

So, which method out of these three will serve HTTP requests the fastest: Web API, OWIN Self Host, or HttpListener? My hypothesis is that HttpListener will be because it the most low-level. The tests for each method will consist of returning the current date and time. There will be no input (no post data or query string) and the result will be returned serialized as JSON. JSON.NET will be used for serialization in each of the projects for consistency. You can get faster performance by using Jil but we’ll leave it alone for this run. I want the out-of-the-box Web API project to be the baseline because that’s what most people are using. The Web API project will be hosted in IIS while the others will be hosted by a Console app. A fourth project will be created which makes 1000 HTTP requests to each host and records the results. The requests will be made from the same machine the servers are running on to eliminate network latency.

Here is my solution with source code for the servers and tester if you’d like to try it for yourself – WebServerComparison.zip

Here are my results: (all times are in seconds)

Web APIOWINHttpListener
Run 10.80594420.69243480.2742231
Run 20.66005780.32892840.1906594
Run 30.6402020.32972160.1872897
Run 40.61898850.34066560.1953822
Run 50.61189960.32807140.1898794
Avg0.667418420.403964360.20748676
It looks like the first run primed our servers since it took considerably longer to complete compared to the ensuing runs. I won’t throw this run out because in the real world you will have to prime your servers for various reasons. It doesn’t affect our outcome either. My hypothesis was correct in that HttpListener was the fastest option. Keep in mind that the difference between HttpListener and Web API/IIS is less than half a microsecond per request but it is a difference nonetheless. I did not show the raw responses but the Web API responses were larger in size because IIS tacks on a couple of headers. This would have made a greater difference if we weren’t making request from the same machine.

As with anything there are some trade-offs. With IIS you get a lot of management features that you would never get by running your own web server. It also has a lot more security and is more robust. It will log requests and handle errors for you. Writing your own web server will give you faster responses but you’ll have to spend time solving problems that IIS has already solved. The trade-off is yours to decide upon. In the case of RulePlex or other extreme performance needy services I think it’s better to go with the faster option.

The OWN self hosting option is neither the fastest nor does it give you any management features. It does mean you can setup your server in more of a Web API way and gives you some added security, but, I don’t think this middle-of-the-road option is worth much. You either want the performance or you want the management. Right?

Other notes

If you have an API that is used by your web app’s front-end via ajax requests and both are on the same domain you should pay attention to the cookies being sent in the request. If possible host the API on a different domain to avoid the cookies from being sent with the request.

Compression may also play a factor in larger requests. My next post will explore compression options.

comments powered by Disqus