Wednesday, November 30, 2011

NodeJS & Simple C# HTTP Server

So... I've read quite a bit about NodeJS and was very early introduced to it by one of my good friends, Dominic ( http://dominicbarnes.us/ ). He's a huge JavaScript fan and so NodeJS was a big hit. I like Javascript, but admittedly I am nowhere near as proficient as he is nor do I understand fully the functional paradigm needed to use it to its full extent. (He's great at pretty much everything open source/other side of the MS/Windows fence, so if you need someone like that go hire him and pay him lots of money - you won't regret it.) NodeJS got attention with it's claims to efficiency with its non-blocking programming style- the functional JavaScript would in theory make writing such code easy.

The Bleeding-edge Event Model in NodeJS?
This was a couples months ago and I was fascinated by the potential NodeJS performance gains and decided I would try writing a simple server in C# with the same model. I figured since C# is compiled, it may be at the least somewhat quicker than the Windows version of NodeJS. Of course, now it all seems silly having learned what I did about IIS through the process. I never realized most of the comparisons for performance were against Apache, and IIS already performs better than Apache anyway. Of course, that seems obvious now- what was I expecting? I suppose at times we can all be suckers for the success stories of the underdog coming out on top, but in practice that is generally not the case. Regardless, it was a fun learning experience. My proof-of-concept server did turn out to be incredibly fast, (and considerably faster than the Windows NodeJS at the time) but that doesn't mean much considering it offered limited functionality. It was a simple test; I used .NET's HTTP Request classes and didn't build my own implementation to keep simple. (which would have been a huge part of the effort) It was really fun trying to think of ways to optimize my little server. (request handling/caching/reading from disk/ect.)

A C# Server Like NodeJS
Basically it is a C# application that only has a task bar tray icon for an interface (I never actually got far enough to turn it into a service and separate the UI from the server service) and sits around waiting for requests. A main thread is the one that just sits around listening to the HTTP port[s] all the time and whenever a requests comes through hands it off to a worker thread. The worker thread checks if the request is cached in memory and if so returns it, if not depending on the extension it will either return a static file, image, or compile the script page, add it to the cache, and return it. (or just run the script page's compiled code if cached)

Having one thread completely devoted to listening for requests and passing the actual request handling to a thread pool allowed my server to respond to requests extremely quickly- this was the concurrency I was after. The cached compiled scripts would run quickly once the compilation for the page script was cached, as it was literally like running a function that returned a string. (Plus obviously the JIT will also compile to native code once the function is called for the first time)

Yes, it compiles on demand! But that wasn't as big a deal as I thought it would be. It was much easier than I thought since .NET comes with compiler libraries for C#. All my server does is some string parsing on the requested file to look for '@{' and '}@' symbols to know where the C# code begins and ends. (C# "script", anyone?) As I mentioned, compiled methods are kept in memory so that subsequent requests are extremely fast.

I realize NodeJS operates a bit differently. NodeJS listens on a single thread (main event loop) and when a request comes in, it immediately processes it. For requests what would require "blocking" functions, (such as File I/O... though technically any function call is blocking by definition), a callback is given and the "blocking" function is queued in a thread pool- this way the main even loop thread goes right back to processing and listening to requests. When the "blocking" function completes, it calls the callback function on the main event loop and the request is finished on that main thread. (As I thought through this I began to realize some potential shortcomings)

In my case, I simply created the event loop to only listen for requests, and then hand off request handling to the thread pool. This is because I couldn't be guaranteed that the page script wouldn't perform a "blocking" operation. However, had I continued with the project and had my intention been to imitate NodeJS exactly, I would have probably needed to build a library of functions that page scripts could have called to handle "blocking" functions. This is where the power or ease of the functional programming style of JavaScript would have been nice. Doing this in C# would have been ugly, but it would be much easier to adopt Javascript's call-back functional style to help segregate those "blocking operations" that should be executed in the thread pool from those that should run in the main event loop. (Technically I didn't have a "main event loop" since all mine did was handle and hand off the requests, but you get the point) In the end, it didn't really matter that I didn't go through all the lengths to simulate that, because for my tests I wrote a page script that didn't do any file I/O or any other "blocking" operations. (Which I suppose makes for poor tests, but good enough for what I needed)

Realizations and Some Final Thoughts on NodeJS & IIS
Throughout the whole time I was researching more about the IIS pipeline and began to realize IIS does pretty much the same thing as my server did as far as listening, handling off requests, and working with a thread pool to process them. (of course, it does it a whole lot better) Eventually I stopped development; the code is here if curious. (Since I abandoned the project, the "scripting" support is very limited- it supports C# since it uses the .NET compiler but the page script code doesn't have access to any server variables like POST/GET, ect making it close to useless)

So NodeJS/IIS thoughts. I think it is a cool technology and I've still got a lot to learn on the subject, but I've researched enough where I feel I have a fair opinion. I think IIS does a pretty darn good job and NodeJS model isn't exactly ground-breaking here... it's actually been around for a long time. Apache is the big web server it always seems to be compared to, and I suppose that's where there's a big win performance-wise is since Apache spawns a new thread for every request. (Maybe the just need to implement a thread pool in their pipeline?) My thoughts are you'd be hard pressed to get test results (and not just mass concurrent request tests) where an equivalent MVC.NET page written well and using IIS under-performs it's equivalent NodeJS page.

In closing, here are a couple articles I agree with, though I think he is rather harsh/offensive to the NodeJS community, but he seems to hit the nail on the head as far as analyzing performance and the NodeJS model:
http://teddziuba.com/2011/10/node-js-is-cancer.html
http://teddziuba.com/2011/10/straight-talk-on-event-loops.html

No comments: