Ayende @ Rahien

Refunds available at head office

Node.cs

No, the title is not a typo. There is so much noise around Node.js, I thought it would be fun to make a sample of how it would work in C# using the TPL. Here is how the hello world sample would look like:

public class HelloHandler : AbstractAsyncHandler
{
    protected override Task ProcessRequestAsync(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        return context.Response.Output.WriteAsync("Hello World!");
    }
}

And the code to make this happen:

public abstract class AbstractAsyncHandler : IHttpAsyncHandler
{
    protected abstract Task ProcessRequestAsync(HttpContext context);

    private Task ProcessRequestAsync(HttpContext context, AsyncCallback cb)
    {
        return ProcessRequestAsync(context)
            .ContinueWith(task => cb(task));
    }

    public void ProcessRequest(HttpContext context)
    {
        ProcessRequestAsync(context).Wait();
    }

    public bool IsReusable
    {
        get { return true; }
    }

    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        return ProcessRequestAsync(context, cb);
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        if (result == null)
            return;
        ((Task)result).Dispose();
    }
}

And you are pretty much done. I combined this with a HttpHandlerFactory which does the routing, and you get fully async, and quite beautiful code.

Comments

Victor Gladkikh
07/29/2011 09:16 AM by
Victor Gladkikh

Ayende, have You seen this https://gist.github.com/1097383 from @thinkbeforecoding?

Steven Robbins
07/29/2011 09:21 AM by
Steven Robbins

Yes, it's very easy to write async web handlers in .net, either using HttpHandlers or the HttpListener (Nancy has both), but it's still using the threadpool, and thread per request, so hardly the same thing as node's event loop.

Lee
07/29/2011 09:25 AM by
Lee

I don't see how this compares to node.js at all.

Sergey Mirvoda
07/29/2011 10:04 AM by
Sergey Mirvoda

it's not the same at all IIRC node on Windows uses IOCP, but TPL uses ThreadPool.

Thilak Nathen
07/29/2011 10:24 AM by
Thilak Nathen

Where's the rest of the code?

p.s. node.cs is already underway https://github.com/Rduerden/Node.cs

Stuart Lodge
07/29/2011 11:06 AM by
Stuart Lodge

Also worth checking out http://nuget.org/List/Packages/SignalR - uses async and await for coding within ASP.NET MVC

Frank
07/29/2011 11:27 AM by
Frank

.NET also uses IOCP underneath, but the events are executed on the default thread pool.

 thinkbeforecoding
07/29/2011 11:47 AM by
thinkbeforecoding

There is also this one (HttpListener + Rx) for Commet style :

https://gist.github.com/1097515

 thinkbeforecoding
07/29/2011 11:50 AM by
thinkbeforecoding

By the way, using Rx doesn't block threads. Only the HttpListenerContext object is kept in memory between request. And you can schedule request handling on your preferred scheduler (Tasks, ThreadPool, new thread... custom ?)

Pablo Cibraro
07/29/2011 01:01 PM by
Pablo Cibraro

Have you seen this implementation made by Jose Romaniello ?

http://joseoncode.com/2011/06/17/event-driven-http-server-in-c-with-rx-and-httplistener/

He built an event driven http server using Rx.

Ayende Rahien
07/29/2011 01:30 PM by
Ayende Rahien

Victor, No, I didn't. I don't like the sync write there, but seems to be in the same vein

Ayende Rahien
07/29/2011 01:31 PM by
Ayende Rahien

Steven, Actually, no. This will not be using the thread pool to wait, it is using overlapped IO, so there is no thread waiting around for this. This is actually quite similar

Ayende Rahien
07/29/2011 01:32 PM by
Ayende Rahien

Sergey, Async operations uses overlapped io, and this uses a thread pool thread only when it is executing your code, not when waiting for anything.

Ayende Rahien
07/29/2011 01:33 PM by
Ayende Rahien

Thilak, My only interest was to show how it can be done, not to actually implement another framework

 thinkbeforecoding
07/29/2011 01:35 PM by
thinkbeforecoding

Actually the write is not sync with the request in the first gist. It's already scheduled on the Task pool.

Steven Robbins
07/29/2011 01:45 PM by
Steven Robbins

I didn't say your code was using the threadpool,but asp.net will still use threadpool threads to handle the initial connection/request, and after you've finished your async work to send the response. This is different to the single threaded event loop/scheduler that node uses.

Something like Kayak (https://github.com/kayak/kayak) is closer to the node model.

 thinkbeforecoding
07/29/2011 02:20 PM by
thinkbeforecoding

Ok for the event loop.. but what is the advantage of using it ?

Alex Simkin
07/29/2011 05:16 PM by
Alex Simkin

@thinkbeforecoding

You can read second paragraph from here http://nodejs.org/#about

Ken Egozi
07/29/2011 06:23 PM by
Ken Egozi

And not a word about Manos?

since manos really use the same libev thing that powers node.js it is actually comparable both in perf and paradigm. You lose the node.js ecosystem (mainly npm) but gain the BCL (with almost every IO thing supporting async)

 thinkbeforecoding
07/29/2011 11:48 PM by
thinkbeforecoding

@alex in that case you can use a loop based scheduler like the Wpf dispatcher or any custom and simple implementation

tobi
07/30/2011 01:13 PM by
tobi

I really like to rub this code into node.js's followers. This stupid fad with node.js kind of makes me angry because I don't understand why even programmers fall for fashion so easily.

"less-than-expert programmers are able to develop fast systems": Well, try to get non-expert users to write code that whose guts have been pulled inside out (callback instead of pull-model).

It makes me angry how useless node.js is. It is outright harmful to use it.

Jack Viers
07/30/2011 06:19 PM by
Jack Viers

Wow tobi. You need to get out more. Please explain how using less memory and non-blocking services is harmful? Are you one of those types that only learn new technology if your company pays for training?

Programmers fall for fashion because it is entirely possible that today's fashion might lead to tomorrow's job. The more diverse your skillet is, the more opportunities you have to do something interesting.

Perhaps you should run a benchmark against node and .net and prove that node is harmful.

Jay Douglass
07/30/2011 06:51 PM by
Jay Douglass

I think there's a fascination with node.js because programmers get tired of doing the same old applications.

Dev: I'm writing a CMS/CRM/Project Management/etc Fanboi dev: Booooring! Dev: I'm doing it with node .js Fanboi dev: Oh wow cool!

tobi
07/30/2011 08:42 PM by
tobi

Jack, you rarely need the 10% in throughput you get from async IO. Most of the time you need developer productivity. See my comment about turning the code inside out. I have written async code myself. It is a terrible exercise. Did you ever writecode that did async IO in a loop? Can't use a while loop cause the function has to exit after every IO...

Remember, performance vs. productivity. Bad trade-off to choose performance most of the time.

dkl
07/31/2011 07:19 AM by
dkl

Ayende, is it possible to estimate memory footprint of node.js and .net async handler for, lets say, 200 simultaneously connected users?

Ayende Rahien
07/31/2011 07:29 AM by
Ayende Rahien

dkl, Without actually running load tests? Not really, I don't expect that to be too high, mind.

John Seymore
07/31/2011 02:27 PM by
John Seymore

"I really like to rub this code into node.js's followers. "

Wow, you sound incredibly sad. There are a lot of defensive MS-stack programmers leaving comments here.

tobi
07/31/2011 02:55 PM by
tobi

John, the right to flame has to be earned. I did earn it with my reasoning about node.js. Feel free to answer my points, then you are allowed to flame.

Chris
07/31/2011 09:06 PM by
Chris

@tobi: The IO facilities in Node let you get all the data at once, in a single callback, or let you use a 'stream', which is basically an event dispatcher that delivers the chunked payloads of the async file reads.

From what I have seen, Node.js makes heavy use of the continuation-passing style (CPS) of programming, which has been prevalent in Lisps (or at least in their compilers) for a great, long while, and allows for tail-call optimizations pretty much everywhere.

And while I agree that to most it looks strange, it makes sense for Node use this style, since JavaScript is a functional programming language. I would imagine that a similar style could easily be written in F# (or even C# with great, heaping piles of Func<> and Action<>), although I admit I am very unfamiliar with F#.

(see also: static single assignment (SSA) and automatic conversion to CPS)

tobi
07/31/2011 10:55 PM by
tobi

Chris, all of what you say is true. You did not make a statement that contradicts what I have said.

I want to address your "streaming" point: Streaming is most useful if the payload is too large to fit in memory. This is a very unusual case. ASP.NET allows for full streaming, though, if you want to. You can even do it asynchronously.

Remember, I fully acknowledge the potential performance improvements of async IO. I just say it is a terrible tradeoff in 99% of all applications. I really mean this number literally. When did you last need async IO? I never did (I implemented it for fun however, so I know what I am talking about).

"I would imagine that a similar style could easily be written in F#": Yes, async worflows are made for exactly that. They are far superior to javascripts CPS. In C# 5 a similar feature will be introduced which will make using node.js not just unwise, but totally absurd.

Ajai
08/01/2011 01:54 PM by
Ajai

Is node.js just asynchrony?

I think it is cool that you can write server side code in Javascript with no context switching between 2 different languages for client & server side code. Maybe there are libraries out there that blur the distinction further, have not researched...

Putting together V8 with async IO is pure genius. Just like Ayende combined a little known Esent library with Lucene.net and got RavenDB.

.NET came with great support for asynchrony even back in the 1.1 days (I personally have used the AsyncPage library from DevelopMentor years back) uses same IAsyncHttpHandler plumbing in this post.

But to get async IO support into languages/frameworks as node.js & Python Torando does is still awesome!

Ajai

tobi
08/01/2011 03:11 PM by
tobi

I would see the point in having node.js with synchroneous IO. This does not preclude concurrency at all (it does not do this in any other synchroneous programming environment either).

Alex Simkin
08/01/2011 03:51 PM by
Alex Simkin

@tobi And what is the point to block event loop for synchroneous IO ?

tobi
08/01/2011 04:13 PM by
tobi

There would be no event loop. It would be a plain old server. The point would be to get node.js's other benefits like using the same language on the server and client.

meh
08/18/2011 06:51 PM by
meh

@tobi

re: "When did you last need async IO? I never did (I implemented it for fun however, so I know what I am talking about)."

Perhaps you should write more successful code. Working in an industry where millions of requests, large web farms, caching at the edge and everywhere in between serving out millions of requests an hour I can tell you there are many great solutions for async IO and I have implemented it so I know what I am talking about.

You can have your opinions, just dont presume they are more correct than anyone elses.

Scott Hanselman
08/28/2011 09:32 PM by
Scott Hanselman

Great stuff! Here's a similar one also: https://github.com/SignalR/SignalR/blob/master/SignalR/Web/HttpTaskAsyncHandler.cs

Robert Hurst
09/03/2011 10:03 PM by
Robert Hurst

Cool, but the problem with C# is its ugly. This is a lot harder to read than a node.js program.

Ayende Rahien
09/04/2011 04:14 AM by
Ayende Rahien

Robert, I actually disagree with you here. I think it is as easy or easier. It just require practice

Comments have been closed on this topic.