Showing posts with label Rx. Show all posts
Showing posts with label Rx. Show all posts

Monday, May 7, 2012

.NET Rx Driven Web Server, Take 2


Feeling like my code was rather crappy, i thought i'd take another stab at the Rx Webserver problem. I was especially unhappy with the async signature of Socket.BeginRecieve. A little research online and i found HttpListener, a .NET class that does the same thing except with a usable Async function (one that returns something meaningful) for my Observeable.FromAsyncPattern.

A Hyper-fast Rx Web Server

This one actually performs! Here's the code:

Edit: I found this after making mine-- José F. Romaniello's implentation of a .NET Rx Web Server. His code looks much more useful, and is a better example of Rx. My version hardly Rx, really. Anyways, i performance tested his as well and the figures were similar to the tests ran on this one below.


Performance Tests-- MVC3, Nodejs, and Our Rx Server

MVC3

Using a generic controller that returns "Thanks!":


Server Software:ASP.NET
Server Hostname:localhost
Server Port:31362
Document Path:/Test/
Document Length:11 bytes
Concurrency Level:1000
Time taken for tests:42.845 seconds
Complete requests:10000
Failed requests:0
Total transferred:2700000 bytes
HTML transferred:110000 bytes
Requests per second:233397.92
Transfer rate:63017.44 kb/s received
Connnection Times (ms)
  min avg max
Connect: 0 0 590
Processing: 688 4077 4649
Total: 688 4077 5239


Our server

Using the code above:

Server Software:Microsoft-HTTPAPI/2.0
Server Hostname:localhost
Server Port:8081
Document Path:/Test/
Document Length:7 bytes
Concurrency Level:1000
Time taken for tests:1.624 seconds
Complete requests:10000
Failed requests:0
Total transferred:1500000 bytes
HTML transferred:70000 bytes
Requests per second:6157021.28
Transfer rate:923553.19 kb/s received
Connnection Times (ms)
  min avg max
Connect: 0 0 2
Processing: 39 155 180
Total: 39 155 182

Node.js

Using the node.js homepage hello world sample:

Server Software:
Server Hostname:localhost
Server Port:1337
Document Path:/
Document Length:8 bytes
Concurrency Level:1000
Time taken for tests:2.066 seconds
Complete requests:10000
Failed requests:0
Total transferred:720000 bytes
HTML transferred:80000 bytes
Requests per second:4839481.65
Transfer rate:348442.68 kb/s received
Connnection Times (ms)
  min avg max
Connect: 0 7 94
Processing: 1 35 244
Total: 1 42 338

The Actual Node.js Beater

Really, there's a good bit of apples to oranges here, but still--can you believe that? I think that means that our little Rx server is C10K compliant! Further, many of our server's metrics beat node.js-- such as transfer rate, requests per second, max times... pretty awesome!

I've still got a ton to learn about Rx... back to the books now.


A .NET Rx Driven Web Server

Edit: please see my other post on creating a .NET Web Server from Rx (Reactive Extensions) since it contains better code.

Although I've seen .Net Rx (Reactive Extensions) around, I never messed with them until today. To me, the concepts behind Rx always seemed self explanatory--perhaps because i have accomplished concurrent apps in .NET 1.0/2.0 without them. However, having spent a little time with them today, I think Rx is good stuff. Honestly, I'm impressed with the interfaces and the services they provide. Let's check it out:

What are the .Net Reactive Extensions(Rx)?

Short answer: pubsub.

Long answer: a ton of sugar over top of .Net streams, async, TPL, and pubsub. I'm not going to get into the generic intros you can find elsewhere that involve streaming enumerables to the console. Instead I'd prefer to create the argument for Rx as such-- when given the need for "X", it is better to provide "the ability to provide X" than "X" itself. The Reactive Extensions give you a ton of really helpful methods to aide you in implementing "the ability to create X" over "X" itself. Allow me to explain--

If i asked you to write me a function that gave me the first 1 million numbers, how would you implement it? I know a younger me would've started on cranking out a for loop, not taking into consideration that decision's implications upon the system's memory. A smarter implementation would be to give me a function/object that gives me the ability to create the first million numbers, perhaps through iterating through the set. Such an object could then forgo the previously mentioned memory issues. The idea of giving "the ability to create/observe X" over "X" itself is arguably the conceptual basis of functional programming's lazy evaluation, which is also what Rx aims to help the user create (to me, at least). So, out of the box you get a ton of ways to create and enable the push/pull of streaming data and/or events.

An Rx TCP Server

The first thing i could think of to make with Rx is a single-threaded TCP server. Maybe that's because when i think of streaming data these days, i tend to think of a node.js style web server. How hard could it be? (And what would the performance be like...?

Version One: A Single-Threaded Non-Rx Readonly TCP Server

If you run the following code, and make a request on your web browser to http://localhost:8081 you'll see the GET request come through to the app.


Version Two: A Single-Threaded Rx Enabled TCP Server

In this version I added two properties to the NetActor-- Incoming and Outgoing. Both are based on new Rx interfaces that allow the client to tap into the push/pull of data to the client. So if you open your web browser, open up the localhost site, and then type into the console app and press enter, it will get delivered to the web page:


Version Three: The Node.js Killer

Ok, so in order to get apache bench to recognize my console app as a web server i had to bind the NetActor's Ip to something other than localhost... not sure why. Once i got that working, i had intermittent failure until I implemented part of the HTTP spec-- at least the response code and connection closed. After that, and also after creating the ability for the NetActor to shut itself down and start itself up, here is what i was left with:


Apache Bench Results





At 500ms+ with a concurrency level of 1, this is definitely not a node.js killer..... ;-)