Scott Hanselman

The Importance (and Ease) of Minifying your CSS and JavaScript and Optimizing PNGs for your Blog or Website

September 02, 2011 Comment on this post [47] Posted in ASP.NET | IIS
Sponsored By

Hello Dear Reader. You may feel free to add a comment at the bottom of this post, something like "Um, DUH!" after reading this. It's funny how one gets lazy with their own website. What's the old joke, "those who can't, teach." I show folks how to optimize their websites all the time but never got around to optimizing my own.

It's important (and useful!) to send as few bytes of CSS and JS and HTML markup down the wire as possible. It's not just about size, though, it's also about the number of requests to get the bits. In fact, that's often more of a problem then file size.

First, go run YSlow on your site.

YSlow such a wonderful tool and it will totally ruin your day and make you feel horrible about yourself and your site. ;) But you can work through that. Eek. First, my images are huge. I've also got 184k of JS, 21k of CSS and 30k of markup. Note my favicon is small. It was  LOT bigger before and even sucked up gigabytes of bandwidth a few years back.

YSlow also tells me that I am making folks make too many HTTP requests:

This page has 33 external JavaScript scripts. Try combining them into one.
This page has 5 external stylesheets. Try combining them into one.

Seems that speeding things up is not just about making things smaller, but also asking for fewer things and getting more for the asking. I want to make fewer request that may have larger payloads, but then those payloads will be minified and then compressed with GZip.

Optimize, Minify, Squish and GZip your CSS and JavaScript

CSS can look like this:

body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}

Or like this, and it still works.

body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}

There's lots of ways to "minify" CSS and JavaScript, and fortunately you don't need to care! Think about CSS/JS minifying kind of like the Great Zip File Wars of the early nineties. There's a lot of different choices, they are all within a few percentage points of each other, and everyone thinks theirs is the best one ever.

There are JavaScript specific compressors. You run your code through these before you put your site live.

And there are CSS compressors:

And some of these integrate nicely into your development workflow. You can put them in your build files, or minify things on the fly.

  • YUICompressor - .NET Port that can compress on the fly or at build time. Also on NuGet.
  • AjaxMin  - Has MSBuild tasks and can be integrated into your project's build.
  • SquishIt - Used at runtime in your ASP.NET applications' views and does magic at runtime.
  • UPDATE: Chirpy - "Mashes, minifies, and validates your javascript, stylesheet, and dotless files."
  • UPDATE: Combres - ".NET library which enables minification, compression, combination, and caching of JavaScript and CSS resources for ASP.NET and ASP.NET MVC web applications."
  • UPDATE: Cassette by Andrew Davey - Does it all, compiles CoffeeScript, script combining, smart about debug- and release-time.

There's plenty of comparisons out there looking at the different choices. Ultimately when compression percentages don't matter much, you should focus on two things:

  • compatibility - does it break your CSS? It should never do this
  • workflow - does it fit into your life and how you work?

For me, I have a template language in my blog and I need to compress my CSS and JS when I deploy my new template. A batch file and command line utility works nicely so I used AjaxMin (yes, it's made by Microsoft, but it did exactly what I needed.)

I created a simple batch file that took the pile of JS from the top of my blog and the pile from the bottom and created a .header.js and a .footer.js. I also squished all the CSS, including my plugins that needed CSS, and put them in one file while being sure to maintain file order.

I've split these lines up for readability only.

set PATH=%~dp0;"C:\Program Files (x86)\Microsoft\Microsoft Ajax Minifier\"

ajaxmin -clobber
scripts\openid.css
scripts\syntaxhighlighter_3.0.83\styles\shCore.css
scripts\syntaxhighlighter_3.0.83\styles\shThemeDefault.css
scripts\fancybox\jquery.fancybox-1.3.4.css
themes\Hanselman\css\screenv5.css
-o css\hanselman.v5.min.css

ajaxmin -clobber
themes/Hanselman/scripts/activatePlaceholders.js
themes/Hanselman/scripts/convertListToSelect.js
scripts/fancybox/jquery.fancybox-1.3.4.pack.js
-o scripts\hanselman.header.v4.min.js

ajaxmin -clobber
scripts/omni_external_blogs_v2.js
scripts/syntaxhighlighter_3.0.83/scripts/shCore.js
scripts/syntaxhighlighter_3.0.83/scripts/shLegacy.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushCSharp.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushPowershell.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushXml.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushCpp.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushJScript.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushCss.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushRuby.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushVb.js
scripts/syntaxhighlighter_3.0.83/scripts/shBrushPython.js
scripts/twitterbloggerv2.js scripts/ga_social_tracker.js
-o scripts\hanselman.footer.v4.min.js

pause
All those ones at the bottom support my code highlighter. This looks complex ,but it's just making three files, two JS and a CSS out of all my mess of required JS files.

This squished all my CSS down to 26k, and here's the output:

CSS
Original Size: 35667 bytes; reduced size: 26537 bytes (25.6% minification)
Gzip of output approximately 5823 bytes (78.1% compression)

JS
Original Size: 83505 bytes; reduced size: 64515 bytes (22.7% minification)
Gzip of output approximately 34415 bytes (46.7% compression)

That also turned 22 HTTP requests into 3.

Optimize your Images (particularly PNGs)

Looks like my 1600k cold (machine not cached) home page is mostly images, about 1300k. That's because I put a lot of articles on the home page but I also use PNGs for images most of my blog posts. I could be more thoughtful and:

  • Use JPEGs for photos of people, things that are visually "busy"
  • Use PNGs for charts, screenshots, things that must be "crystal clear"

I can also optimize the size of my PNGs (did you know you can do that!) before I upload them with PNGOUT. For bloggers and for ease I recommend PNGGauntlet, which is a Windows app that calls PNGOut for you.  Easier than PowerShell, although I do that also.

If you use Visual Studio 2010, you can use Mad's Beta Image Optimizer Extension that will let you optimize images directly from Visual Studio.

To show you how useful this is, I downloaded the images from the last month or so of posts on this blog totaling 7.29MB and then ran them through PNGOut via PNGGauntlet.

All my PNGs getting in line to be optimzed inside of PNGGauntlet

Then I took a few of the PNGs that were too large and saved them as JPGs. All in all, I saved 1359k (that's almost a meg and a half or almost 20%) for minimal extra work.

If you think this kind of optimization is a bad idea, or boring or a waste of time, think about the multipliers. You're saving (or I am) a meg and a half of image downloads, thousands of times. When you're dead and gone your blog will still be saving bytes for your readers! ;)

This is important not just because saving bandwidth is nice, but because perception of speed is important. Give the browser less work to do, especially if, like me, almost 10% of your users are mobile. Don't make these little phones work harder than they need to and remember that not everyone has an unlimited data plan.

Let your browser cache everything, forever

Mads reminded me about this great tip for IIS7 that tells the webserver to set the "Expires" header to a far future date, effectively telling the browser to cache things forever. What's nice about this is that if you or your host is using IIS7, you can change this setting yourself from web.config and don't need to touch IIS settings.

<staticContent>
<clientCache httpExpires="Sun, 29 Mar 2020 00:00:00 GMT" cacheControlMode="UseExpires" />
</staticContent>

You might think this is insane. This is, in fact, insane. Insane like a fox. I built the website so I want control. I version my CSS and JS files in the filename. Others use QueryStrings with versions and some use hashes. The point is are YOU in control or are you just letting caching happen? Even if you don't use this tip, know how and why things are cached and how you can control it.

Compress everything

Make sure everything is GZip'ed as it goes out of your Web Server. This is also easy with IIS7 and allowed me to get rid of some old 3rd party libraries. All these settings are in system.webServer.

<urlCompression doDynamicCompression="true" doStaticCompression="true" dynamicCompressionBeforeCache="true"/>

If there is one thing you can do to your website, it's turning on HTTP compression. For average pages, like my 100k of HTML, it can turn into 20k. It downloads faster and the perception of speed by the user from "click to render" will increase.

Certainly this post just scratches the surface of REAL performance optimization and only goes up to the point where the bits hit the browser. You can go nuts trying to get an "A" grade in YSlow, optimizing for # of DOM objects, DNS Lookups, JavaScript ordering, and on and on.

That said, you can get 80% of the benefit for 5% of the effort by these tips. It'll take you no time and you'll reap the benefits hourly:

  • Minifying your JS and CSS
  • Combining CSS and JS into single files to minimize HTTP requests
  • Turning on Gzip Compression for as much as you can
  • Set Expires Headers on everything you can
  • Compress your PNGs and JPEGs (but definitely your PNGs)
  • Use CSS Sprites if you have lots of tiny images

I still have a "D" on YSlow, but a D is a passing grade. ;) Enjoy, and leave your tips, and your "duh!" in the comments, Dear Reader.

Also, read anything by Steve Souders. He wrote YSlow.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Asynchronous scalable web applications with real-time persistent long-running connections with SignalR

August 29, 2011 Comment on this post [92] Posted in ASP.NET | IIS | Javascript | SignalR
Sponsored By

I've been spending some time exploring asynchrony and scale recently. You may have seen my post about my explorations with node.js and iisnode running node on Windows.

Every application has different requirements such that rules to "make it scale" don't work for every kind of application. Scaling a web app that gets some data and for loops over it is different from an app that calls out to a high-latency mainframe is different from an app that needs to maintain a persistent connection to the server.

The old adage "when all you have it is a hammer everything looks like a nail" really holds true in the programming and web space. The more tools - and the knowledge to use them - the better. That's why I'm an advocate not only of polyglot programming but also of going deep with your main languages. When you really learn LINQ for example and get really good at dynamics, C# becomes a much more fun and expressive language.

Polling is a common example of hammering a screw. Trying to make a chat program? Poll every 5 seconds. Got a really long running transaction? Throw up an animated GIF and poll until eternity, my friend!

Long polling is another way to get things done. Basically open a connection and keep it open, forcing the client (browser) to wait, pretending it's taking a long time to return. If you have enough control on your server-side programming model, this can allow you to return data as you like over this "open connection." If the connection breaks, it's transparently re-opened and the break is hidden from both sides. In the future things like WebSockets will be another way to solve this problem when it's baked.

Persistent Connections in ASP.NET

Doing this kind of persistent connection in a chat application or stock ticker for example hasn't been easy in ASP.NET. There hasn't been a decent abstraction for this on the server or a client library to talk to it.

SignalR is an asynchronous signaling library for ASP.NET that our team is working on to help build real-time multi-user web application.

Isn't this just Socket.IO or nowjs?

Socket.IO is a client side JavaScript library that talks to node.js. Nowjs is a library that lets you call the client from the server. All these and Signalr are similar and related, but different perspectives on the same concepts. Both these JavaScript libraries expect certain things and conventions on the server-side, so it's probably possible to make the server look the way these clients would want it to look if one wanted.

SignalR is a complete client- and server-side solution with JS on client and ASP.NET on the back end to create these kinds of applications. You can get it up on GitHub.

But can I make a chat application in 12 lines of code?

I like to say

"In code, any sufficient level of abstraction is indistinguishable from magic."

That said, I suppose I could just say, sure!

Chat.DoItBaby()

But that would be a lie. Here's a real chat application in SignalR for example:

Client:

var chat = $.connection.chat;
chat.name = prompt("What's your name?", "");

chat.receive = function(name, message){
$("#messages").append("
"+name+": "+message);
}

$("#send-button").click(function(){
chat.distribute($("#text-input").val());
});

Server:

public class Chat : Hub {
public void Distribute(string message) {
Clients.receive(Caller.name, message);
}
}

That's maybe 12, could be 9, depends on how you roll.

More details on SignalR

SignalR is broken up into a few package on NuGet:

  • SignalR - A meta package that brings in SignalR.Server and SignalR.Js (you should install this)
  • SignalR.Server - Server side components needed to build SignalR endpoints
  • SignalR.Js - Javascript client for SignalR
  • SignalR.Client - .NET client for SignalR
  • SignalR.Ninject - Ninject dependeny resolver for SignalR

If you just want to play and make a small up, start up Visual Studio 2010.

First, make an Empty ASP.NET application, and install-package SignalR with NuGet, either with the UI or the Package Console.

Second, create a new default.aspx page and add a button, a textbox, references to jQuery and jQuery.signalR along with this script.









    Low Level Connection

    Notice we're calling /echo from the client? That is hooked up in routing in Global.asax:

    RouteTable.Routes.MapConnection("echo", "echo/{*operation}");

    At this point, we've got two choices of models with SignalR. Let's look at the low level first.

    using SignalR;
    using System.Threading.Tasks;

    public class MyConnection : PersistentConnection
    {
    protected override Task OnReceivedAsync(string clientId, string data)
    {
    // Broadcast data to all clients
    return Connection.Broadcast(data);
    }
    }

    We derive from PersistentConnection and can basically do whatever we want at this level. There's lots of choices:

    public abstract class PersistentConnection : HttpTaskAsyncHandler, IGroupManager
    {
    protected ITransport _transport;

    protected PersistentConnection();
    protected PersistentConnection(Signaler signaler, IMessageStore store, IJsonStringifier jsonStringifier);

    public IConnection Connection { get; }
    public override bool IsReusable { get; }

    public void AddToGroup(string clientId, string groupName);
    protected virtual IConnection CreateConnection(string clientId, IEnumerable groups, HttpContextBase context);
    protected virtual void OnConnected(HttpContextBase context, string clientId);
    protected virtual Task OnConnectedAsync(HttpContextBase context, string clientId);
    protected virtual void OnDisconnect(string clientId);
    protected virtual Task OnDisconnectAsync(string clientId);
    protected virtual void OnError(Exception e);
    protected virtual Task OnErrorAsync(Exception e);
    protected virtual void OnReceived(string clientId, string data);
    protected virtual Task OnReceivedAsync(string clientId, string data);
    public override Task ProcessRequestAsync(HttpContext context);
    public void RemoveFromGroup(string clientId, string groupName);
    public void Send(object value);
    public void Send(string clientId, object value);
    public void SendToGroup(string groupName, object value);
    }

    High Level Hub

    Or, we can take it up a level and just do this for our chat client after adding

    <script src="/signalr/hubs" type="text/javascript"></script>

    to our page.

    $(function () {
    // Proxy created on the fly
    var chat = $.connection.chat;

    // Declare a function on the chat hub so the server can invoke it
    chat.addMessage = function (message) {
    $('#messages').append('
  • ' + message + '');
    };

    $("#broadcast").click(function () {
    // Call the chat method on the server
    chat.send($('#msg').val());
    });

    // Start the connection
    $.connection.hub.start();
    });
  • Then there is no need for routing and the connection.chat will map to this on the server, and the server can then call the client back.

    public class Chat : Hub
    {
    public void Send(string message)
    {
    // Call the addMessage method on all clients
    Clients.addMessage(message);
    }
    }

    At this point your brain should have exploded and leaked out of your ears. This is C#, server-side code and we're telling all the clients to call the addMessage() JavaScript function. We're calling the client back from the server by sending the name of the client method to call down from the server via our persistent connection. It's similar to NowJS but not a lot of people are familiar with this technique.

    SignalR will handle all the connection stuff on both client and server, making sure it stays open and alive. It'll use the right connection for your browser and will scale on the server with async and await techniques (like I talked about in the node.js post where I showed scalable async evented I/O on asp.net).

    Want to see this sample running LIVE?

    We've got a tiny tiny chat app running on Azure over at http://jabbr.net/, so go beat on it. There are folks in /join aspnet. Try pasting in YouTube links or images!

    SignalR Chat

    It's early, but it's an interesting new LEGO piece for .NET that didn't completely exist before. Feel free to check it out on GitHub and talk to the authors of SignalR, David Fowler and Damian Edwards. Enjoy.

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    Installing and Running node.js applications within IIS on Windows - Are you mad?

    August 28, 2011 Comment on this post [59] Posted in IIS | nodejs | Open Source
    Sponsored By

    iisnode registering node.js in IIS management Some folks on our team have been working on making node.js work awesomely on Windows. There's a few questions you might have.

    First, what's node.js?

    If you're not familiar with node.js, it's a new web programming toolkit that everyone's talking about. It's the one that makes you feel not hip if you don't know what it is. Like Ruby on Rails was a few years back. Folks called it "Node" and it's basically server-side JavaScript. The idea is that if you are doing a bunch of JavaScript on the client and you do JavaScript all day, why not do some JavaScript on the server also. One less thing to learn, I suppose.

    If you are an ASP.NET programmer, you can think of node.js as being like an IHttpHandler written in JavaScript. For now, it's pretty low-level. It's NOT an HttpHandler, but I'm using an analogy here, OK? Here's a lovely article by Brett McLaughlin that goes into more detail about Node.js and what it is. His subtitle is "Node isn't always the solution, but it does solve some important problems" and that's just exactly it.

    UPDATE 1: Why does node.js matter?

    Why bother with node at all? There's a number of interesting aspects to node as it sits. It uses a very fast JavaScript engine called V8, but more importantly its I/O is asynchronous and event-driven which contrasts with typical synchronous code.

    For example, a naive hello world HttpHandler in ASP.NET that "does some work" for a few seconds (gets a file, accesses a service, etc) could look something like this:

    public class SimpleHandler : IHttpHandler 
    {
    public void ProcessRequest(HttpContext context)
    {
    Thread.Sleep(2000); //Do something that takes a while
    context.Response.Write("Hello from SimpleHandler");
    }

    public bool IsReusable { get { return true; } }
    }

    And this is usually fine for most stuff. However, when I push this HARD with a load testing tool and a thousand virtual clients, I can barely get 60 requests a second. The request thread is tied up waiting for the "work" to happen and everyone else gets in line. I'm using up ASP.NET pool. It'd be nice if the work would get handled and someone would "call me back" when it's finished. It's like waiting on hold for tech support. You are effectively blocked as you wait for them to pick up their end. Wouldn't it be nice if they just called you back when they were ready?

    ASP.NET has always been able to do things (see this MSDN article from 2003 on Async Handlers) with IHttpAsyncHandler but it's always been a bit hard and almost no one knows about it. With the Async CTP and the Task libraries built into .NET, you can build a nicer abstraction on top of IHttpAsyncHandler. Ayende has a simple example AbstractAsyncHandler (there's many of these out there, including a few in our own tools, some things in MVC, and some things in SignalR (more on that soon)) that we can use to do similar work. This example could also do other more complex and pertinent things like file IO, db IO or calling a web service. This is a naive example that doesn't map exactly to the node one below, but it makes the point. Plus, it's nice to look at.

    public class SimpleAsyncAyendeHandler : AbstractAsyncHandler
    {
    protected override async Task ProcessRequestAsync(HttpContext context)
    {
    await TaskEx.Delay(2000);
    await context.Response.Output.WriteAsync("Hello from Ayende and Scott");
    }
    }

    Pointing the same 1000 virtual clients at this handler gives me 500 requests a second, which makes sense as a request takes 2 seconds to finish. If we were doing I/O or other more complex and long running things than waiting, this scales better than the first example. Doing asynchronous code in .NET as well as parallelism is much easier than before, as evidenced by the two lines of  code above and the simplicity of Ayende's small example. The fact that this kind of thing is easy and elegant in node is an attractive thing about node.

    Node loves asynchrony, and uses JavaScript callbacks to provide asynchrony in a pleasant way. You use events and callbacks in JavaScript already on the client, why not use them on the server? Here's an example from Marc Fasel's blog on the topic.

    First, some synchronous file work via Marc:

    var fs = require('fs'), filenames, i, processId;

    filenames = fs.readdirSync(".");
    for (i = 0; i < filenames.length; i++) {
    console.log(filenames[i]);
    }
    console.log("Ready.");

    processId = process.getuid();

    And the same work using an asynchronous pattern that may look familiar!

    var fs = require('fs'), processId;

    fs.readdir(".", function (err, filenames) {
    var i;
    for (i = 0; i < filenames.length; i++) {
    console.log(filenames[i]);
    }
    console.log("Ready.");
    });

    processId = process.getuid();

    The I/O happens and the callback function that's dependant on the result is executed when the I/O is finished. Powerful stuff.

    Why would I want node.js to run on Windows and IIS?

    Tomasz Janczuk is working on the iisnode project lately. You might think that Windows and node don't belong together. "That's just wrong! What are they thinking? I thought IIS was all about .NET?" Well, you may recall I spoke at CodeMash a few years back on IIS7 and PHP and did a screencast showing how IIS7, PHP and FastCGI could push many thousands of requests a second. The IIS folks, the Windows folks, the Azure folks, want to make sure everything runs well on Windows. Remember, we sell Windows, so it's good if it does many things well. ;)

    Why bother getting node to run on IIS? Tomasz says it best:

    Some of the advantages of hosting node.js applications in IIS using the iisnode module as opposed to self-hosting node.exe processes include:

    • Process management. The iisnode module takes care of lifetime management of node.exe processes making it simple to improve overall reliability. You don’t have to implement infrastructure to start, stop, and monitor the processes.
    • Scalability on multi-core servers. Since node.exe is a single threaded process, it only scales to one CPU core. The iisnode module allows creation of multiple node.exe processes per application and load balances the HTTP traffic between them, therefore enabling full utilization of a server’s CPU capacity without requiring additional infrastructure code from an application developer.
    • Auto-update. The iisnode module ensures that whenever the node.js application is updated (i.e. the script file has changed), the node.exe processes are recycled. Ongoing requests are allowed to gracefully finish execution using the old version of the application, while all new requests are dispatched to the new version of the app.
    • Access to logs over HTTP. The iisnode module provides access the output of the node.exe process (e.g. generated by console.log calls) via HTTP. This facility is key in helping you debug node.js applications deployed to remote servers.
    • Side by side with other content types. The iisnode module integrates with IIS in a way that allows a single web site to contain a variety of content types. For example, a single site can contain a node.js application, static HTML and JavaScript files, PHP applications, and ASP.NET applications. This enables choosing the best tools for the job at hand as well progressive migration of existing applications.
    • Minimal changes to node.js application code. The iisnode module enables hosting of existing HTTP node.js applications with very minimal changes. Typically all that is required is to change the listed address of the HTTP server to one provided by the iisnode module via the process.env.PORT environment variable.
    • Integrated management experience. The issnode module is fully integrated with IIS configuration system and uses the same tools and mechanism as other IIS components for configuration and maintenance.

      In addition to benefits specific to the iisnode module, hosting node.js applications in IIS allows the developer to benefit from a range of IIS features, among them:

      • port sharing (hosting multiple HTTP applications over port 80)
      • security (HTTPS, authentication and authorization)
      • URL rewriting
      • compression
      • caching
      • logging

    These are all compelling, but the most interesting bit here, in my opinion, is integration. The iisnode module is a proper IIS module, just like ASP.NET and PHP. This means you can have a single website that has multiple kinds of content. Restated from above:

    For example, a single site can contain a node.js application, static HTML and JavaScript files, PHP applications, and ASP.NET applications.

    Sometimes folks freak out when I say you can have an ASP.NET WebForms app and a ASP.NET MVC app in the same AppPool as a "hybrid." Frankly, Dear Reader, people don't even realize the power and flexibility of IIS. When you plug in something new like node but run it the way you run other things it inherits all the coolness of the outer container, in this case, IIS.

    Fine, you got me, how do I run node.js on Windows?

    I'm assuming you are running IIS7.

    • Go download node.exe, and put it in c:\node
    • Go download a build of iisnode.
    • Unzip iisnode's zip into \inetpub\iisnode
      • (that was my idea, not sure if it's the best place)
    • From an Administrator Command Line, run install.bat.

    The install.bat will:

    • unregister existing "iisnode" global module from your installation of IIS if such registration exists
    • register iisnode as a native module with your installation of IIS
    • install configuration schema for the "iisnode" module
    • remove existing "iisnode" section from system.webServer section group in applicationHost.config
    • add the "iisnode" section within the system.webServer section group in applicationHost.config
    • delete the iisnode web application if it exists
    • add a new site iisnode to IIS

    No warranties! Be careful, you're living on the edge. Remember, you're reading this stuff on some random dude's blog.

    WARNING: I couldn't figure out the right permissions for the AppPool and the File System so I wimped out and gave my local AppPool "SYSTEM" permissions. This is awful and totally my fault. I filed an issue on the iisnode GitHub and I'll fix it and update this post when I hear back.

    I made a new AppPool just for node, gave it SYSTEM access, then assigned the Node Site to this new AppPool. Your site should look like:

    Node Site in IIS7

    And if you click on Modules for this Site in IIS7 you should see iisnode as a native module:

    Hey, it's iisnode as a native module, just like I said. Crazy.

    At this point, you should be able to hit http://localhost/node/helloworld/hello.js and get back:

    Hello, world! [helloworld sample]

    The contents of which are simply:

    var http = require('http');

    http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello, world! [helloworld sample]');
    }).listen(process.env.PORT);

    Lovely.

    Fooling around with WCAT (Web Capacity Analysis Tool) and node.

    Disclaimer: To be clear, this is so very fooling around. This is just to show that it works and it can do the basics really fast. I'm not doing a benchmark nor am I saying "this works better than this other thing." Remember, they just got started recently porting node itself to Windows, and Tomasz and friends are just beginning their work. So, don't overreach. That said, the preliminary work they are doing is really impressive.

    I couldn't help myself. I mean, it's one thing to install a helloworld of some new thing, run it once and go "OK, that runs." It's another to pound it until it says "Uncle." After I got the hello world stuff working, I wanted to do some poor man's stress testing to see what the players involved did.

    First, I installed WCAT, a free Web Capacity Analysis Tool from the IIS team.

    1. WCAT 6.3 x86
    2. WCAT 6.3 x64

    Warning. This is a command-line only tool and it's really persnickety when you run it. It's confusing and it took me a minute to setup.  Here's the steps I took after installing. This is all from an Administrator Command Prompt. Note also that I'm doing this all on one machine, which is cheesy, but remember, it is a GOM.

    1. cscript //H:Cscript
    2. wcat.wsf –terminate –update –clients localhost
    3. Then I made a folder I called \nodetests and I put these three files in it:

    wcat.bat

    pushd C:\Users\Scott\Desktop\nodetests
    "c:\program files\wcat\wcat.wsf" -terminate -run -clients localhost -f settings.ubr -t nodescenario.ubr -s localhost -singleip -o C:\Users\Scott\Desktop\nodetests
    pause

    nodescenario.ubr (or call it whatever you want)

    This is so basic. It just beats on the four sample applications for a while.

    scenario
    {
    name = "node_fun";

    warmup = 30;
    duration = 90;
    cooldown = 30;

    default
    {
    setheader
    {
    name = "Connection";
    value = "keep-alive";
    }
    setheader
    {
    name = "Host";
    value = server();
    }
    version = HTTP11;
    statuscode = 200;
    close = ka;
    }

    transaction
    {
    id = "foo";
    weight = 1000;
    request
    {
    url = "/node/logging/hello.js";
    }
    }
    transaction
    {
    id = "bar";
    weight = 2000;
    request
    {
    url = "/node/helloworld/hello.js";
    }
    }
    transaction
    {
    id = "baz";
    weight = 2000;
    request
    {
    url = "/node/defaultdocument/";
    }
    }
    transaction
    {
    id = "bat";
    weight = 2000;
    request
    {
    url = "/node/configuration/hello.js";
    }
    }
    }

    settings.ubr

    I just copied in the one from samples and uncommented out and changed (and tweaked during testing) these lines:

    server         = "hexpower7";
    clients = 1;
    virtualclients = 8;

    Now, run the Test

    Next, I ran wcat.bat as an Administrator...you can see all the little node.exe's firing up. I've got a

    (Remember they are running as SYSTEM because I was unable to figure out the right permissions. That's my bad, no one else's. I'll figure it out one day.)

    Lots of little node processes

    Here's the WCAT tool's console output...I'm able to consistently do 10,000 hello worlds a second and ended up with just under a million normal requests and responses in 90 seconds. That's a lot of hello worlds.

    Remember Hanselman's Rule of Scale.

    "If you do nothing, you can scale infinitely." - Me

    Of course, this is all local on a fast machine. This is just hello world (with some logging) so it's not testing node much, nor IIS much, but rather the collaboration between the whole system, IIS, iisnode, and node itself.

    Aside: an ASP.NET IHttpHandler doing the exact same thing on this same machine gets 22,500 requests a second, so node and iisnode has some room to improve, which is great.

    Here's the node/iisnode results:

    Pushing a million transactions in 90 seconds

    There's a lot of things I could configure on both sites, number of clients, virtual clients, as well as iisnode-specific settings (which are, nicely enough, managed in a web.config:

    <configuration>
    <system.webServer>
    <handlers>
    <add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
    </handlers>
    <iisnode
    nodeProcessCommandLine="%systemdrive%\node\node.exe"
    maxProcessCountPerApplication="4"
    maxConcurrentRequestsPerProcess="1024"
    maxPendingRequestsPerApplication="1024"
    maxNamedPipeConnectionRetry="3"
    namedPipeConnectionRetryDelay="2000"
    asyncCompletionThreadCount="4"
    initialRequestBufferSize="4096"
    maxRequestBufferSize="65536"
    uncFileChangesPollingInterval="5000"
    gracefulShutdownTimeout="60000"
    loggingEnabled="true"
    logDirectoryNameSuffix="logs"
    maxLogFileSizeInKB="128"
    appendToExistingLog="false"
    />
    </system.webServer>
    </configuration>

    This is pretty cool stuff. I like that the team I work with is excited to make things work well on IIS and I'm stoked that I can mess around with node now without firing up VMs. I'll report back as I learn more!

    Related Links

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    NerdDinner being updated to MVC3 with Razor, HTML5, GeoLocation, EF CodeFirst, jQuery Mobile, YepNope and Modernizr and a fixed Mobile Device Capabilities ViewEngine

    August 27, 2011 Comment on this post [28] Posted in ASP.NET | ASP.NET MVC | HTML5 | IE9 | Mobile | NerdDinner
    Sponsored By

    NerdDinner with jQuery Mobile on an iPhone asking for my locationTwo years ago Rob, Phil, and I released our MVC 1.0 book with the Gu helping with the big intro. I created the basic Nerd Dinner sample application (code here) and released the first 185 pages for free. Since the initial 1.0 release, we've had help from lots of people like Dave Ward, Andrew Aarnott and Jon Galloway on a lot of little things like JavaScript and OpenAuth support, then John V. Petersen moving us to MVC3 with Razor Views, and Peter Mourfield taking on lots of JavaScript and new features. These guys started working on a feature here and a feature there in our spare time. It's been a collaborative effort with folks dropping in, contributing here and there with long periods of silence in between.

    After John V. Petersen moved us to MVC3 and Razor, Peter Mourfield got on a feature tear and now we've added or updated:

    Now, to be clear, this isn't a release, it's an initial almost release. Call it an Alpha. That said, it's up live at http://www.nerddinner.com and the source continues to move forward in the Trunk on Codeplex. We've got some inconsistencies with the mobile site and back  button, and geoloc is not working completely on an iPhone but it's decent on a browser. We're working on this an hour here and an hour there, but if you, Dear Reader, find any bugs in the trunk or obvious stupid mistakes, please to let us know and *cough* submit a patch *cough*. Or at least leave a nice Issue we can track.

    As we get these features working rock solid, Pete and I will do a series of posts digging in to what worked well and what didn't in each feature. Already Pete has a good blog post talking about adding HTML5 Geolocation to Nerd Dinner with yepnope.js, and Modernizr. He used yepnope, a great library for saying "do you support this feature? Yep? Get this JS. Nope? Get this JS. For example:

    <script type="text/javascript">
    $(document).ready(function () {
    yepnope({
    test: Modernizr.geolocation,
    yep: '@Url.Content("~/Scripts/geo.js")',
    nope: '@Url.Content("~/Scripts/geo-polyfill.js")',
    callback: function (url, result, key) {
    getCurrentLocation();
    }
    });
    });
    </script>

    Love it. More details and code on Pete's blog post. In the image below you can see IE9 warning me that my local site wants to track my physical location.

    Geolocation in IE9

    Here's Firefox prompting me for a location on the live site:

    Firefox wants to know where I am

    And Chrome:

    Chrome knows where you live!

    As I said, there are surely lots of bugs and subtleties we need to iron out, but I'm glad we're actually moving this sample forward. Hope you enjoy, and do feel free to fix our bugs for us, Dear Reader. ;)

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    Hanselminutes Podcast Episode Rollup 273 through 280 - Glimpse, JavaScript, Kinect, Script#, PolyGlot, Azure, Windows and Graph Databases

    August 26, 2011 Comment on this post [0] Posted in Podcast
    Sponsored By

    It's happened again, I've gotten behind on my Podcast posts and rather than flooding you with copy-pastes, I figured I'd do a rollup post. It's a shame because, if I may say so, I think it's been a great few months for the show. Perhaps I'll start doing rollups every four episodes.

    If you haven't listen to the show before, or perhaps listened in the past and stopped, now is a great time to get back on board.  Also, please do take a moment and review the show on iTunes. The show is limited to about 30 minutes long, so it's perfect for your work commute.

    You can subscribe easily in a number of ways:

    Subscribe: Subscribe to Hanselminutes or Subscribe to my Podcast in iTunes or Zune

    Here's what's been going on with the last few episodes:

    Haixun Wang #280 Microsoft Research: Trinity is a Graph Database and a Distributed Parallel Platform for Graph Data

    Scott talks via Skype to Haixun Wang at Microsoft Research Asia about Trinity: a distributed graph database and computing platform. What is a GraphDB? How is it different from a traditional Relational DB, a Document DB or even just a naive in-memory distributed data structure? Will your next database be a graph database?


    Within Windows#279 Within Windows with Rafael Rivera

    Scott sits down with Rafael Rivera to talk about the black box that is Windows. Or is it? Rafael doesn't take no for an answer and shares stories of breaking apps to fix them. No more secrets, this week on Hanselminutes.


    Windows Azure Logo#278 Microsoft Web Platform and Azure direction with Scott Hunter

    Scott Hanselman and Scott Hunter (also known as Scotts the Lesser) talk about recently Azure/Web reorg, the direction that ASP.NET and Azure are talking, and how they see open source fitting into the future at Microsoft.


    #277 Polyglot Programming and .NET - Lessons Learned with Ivan Towlson from Mindscape

    Mindscape LogoScott sits down with Ivan Towlson from Mindscape. They recently released Web Workbench to the community for free with support for LESS, SASS, and CoffeeScript. Interestingly, they used C#, F#, JavaScript and Ruby to create this app. Why was polyglot programming right for what them? Is it right for you?


    Earth Class Mail Logo#276 Script# compiles to JavaScript: A Real World Implementation at Earth Class Mail

    Scott talks to Matt Clay and Matt Davis at Earth Class Mail about how they used Nikhil Kothari's Script# compiler to write JavaScript from C# source. Why did they do it? What were the benefits? The problems? Would they do it again?


    Kinect#275 Digging into the Kinect SDK with Dan Fernandez

    Scott gets schooled on the Microsoft Research Kinect SDK by Dan Fernandez. What happens when I plug a Kinect into my PC? What's included with the SDK and what's not? What work happens in the hardware and what happens in software...and more importantly, what can I build?


    Semantic Markup#274  JavaScript is Assembly Language for the Web: Semantic Markup is Dead! Clean vs. Machine-coded HTML

    Scott talks to Erik Meijer about the idea that JavaScript is an assembly language. What assumptions can we make and how could this idea fundamentally change how we develop software on the web?


    Glimpse#273 Glimpse - A client-side Glimpse into your server

    Scott talks with open source developers Anthony van der Hoorn and Nik Molnar from the Glimpse Project. Their very innovative (and all JavaScript and HTML!) debugger tool for ASP.NET has taken the community by storm. How did they do it and how can Glimpse make your live better?


    Also, don't forget that Telerik is our sponsor and we love them.

    Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Forms and WPF. Enjoy developer tools like.NET Reporting, ORM, Automated Testing Tools, Agile Project Management Tools, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visit www.telerik.com.

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.