Thursday, August 25, 2011

Hosting node.js applications in IIS on Windows

In this post I am discussing hosting node.js appplications in IIS on Windows using the iisnode project I have been lately working on.

What benefits does iisnode provide?

The iisnode project provides a native IIS 7.x module that allows hosting of node.js applications in IIS. The project utilizes the Windows build of node.exe that has recently seen major improvements.

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

Hello World

Follow the installation instructions at the bottom of  iisnode project site to get the module and samples installed on your Windows box with IIS7 enabled.

The hello world sample consists of two files: hello.js and web.config.

This is the hello.js file from the helloworld sample:

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);

You will notice that the only difference between this code and the hello world sample from the front page of http://nodejs.org is in the specification of the listening address for the HTTP server. Since IIS controls the base address for all HTTP listeners, a node.js application must use the listen address provided by the iisnode module through the process.env.PORT environment variable rather than specify its own.

The web.config file is required to instruct IIS that the hello.js file contains a node.js application. Otherwise IIS would consider this file to be client side JavaScript and serve it as static content. The web.config designates hello.js as a node.js application by scoping the registration of the handler in the iisnode module to that file only:

<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</configuration>

This handler registration allows the same web site to contain other *.js files (e.g. jQuery libraries) that IIS will continue serving as static files.

What else can iisnode do?

Scalability on multi-core servers

For every node.js application (e.g. hello.js above), iisnode module can create many node.exe process and load balance traffic between them. Each node.exe process can accommodate a configurable number of concurrent requests (maxConcurrentRequestsPerProcess setting). If all existing processes serving a particular application have reached this quota, iisnode will create a new node.exe process for that application, up to a configurable number of processes per application (maxProcessCountPerApplication setting). In addition, when the overall concurrent active request quota has been reached for an application (maxConcurrentRequestsPerProcess * maxProcessCountPerApplication), new incoming requests are temporarily queued up in a pending request queue of configurable size (maxPendingRequestsPerApplication setting), where they wait to be dispatched to one of the node.exe processes as soon as they finish processing a currently active request. Only once this queue has also reached its limit will iisnode module start rejecting new HTTP requests with a 503 (Server Too Busy) status code. Requests are dispatched across multiple node.exe processes serving a node.js application with a round-robin load balancing algorithm.

Auto-update

Whenever the JavaScript file with a node.js application changes (as a result of a new deployment), the iismodule will gracefully upgrade to the new version. All node.exe processes running the previous version of the application that are still processing requests are allowed to gracefully finish processing in a configurable time frame (gracefulShutdownTimeout setting). All new requests that arrive after the JavaScript file has been updated are dispatched to a new node.exe process that runs the new version of the application.

Changes in the JavaScript file are detected regardless if the file resides on a local file system or a UNC share, but the underlying mechanisms are different. In case of a local file system, an OS level directory watching mechanism is used which provides low latency, asynchronous notifications about file changes. In case of files residing on a UNC share, file timestamps are periodically polled for changes with a configurable interval (uncFileChangesPollingInterval setting).

Access to logs over HTTP

To help in ‘console.log’ debugging, the iisnode module redirects output generated by node.exe processes to stdout or stderr to a text file. IIS will then serve these files as static textual content over HTTP. Capturing stdout and stderr in files is controlled with a configuration setting (loggingEnabled). If enabled, iisnode module will create a per-application special directory to store the log files. The directory is located next to the *.js file itself and its name is created by concatenating the *.js file name with a configurable suffix (logDirectoryNameSuffix setting). The directory will then contains one text file per node.exe process dedicated to running this node.js application; these files are named 0.txt, 1.txt, etc. with the file name being the ordinal number of the process serving this application (up to but not including maxProcessCountPerApplication). The logs can be accessed from the browser using HTTP: given a node.js application available at http://mysite.com/foo.js, the output of the first process serving this application would by default be located at http://mysite.com/foo.js.logs/0.txt.

The logDirectoryNameSuffix is configurable to allow for obfuscation of the log location in cases when the service is publicly available. In fact, it can be set to a cryptographically secure or otherwise hard to guess string (e.g. GUID) to provide a pragmatic level of logs privacy. For example, by setting logDirectoryNameSuffix to ‘A526A1F2-4E22-4488-B930-6A71CC7649CD’ logs would be exposed at http://mysite.com/foo.js.A526A1F2-4E22-4488-B930-6A71CC7649CD/0.txt.

Log files are not allowed to grow unbounded. The maxLogFileSizeInKB setting controls the maximum size of a log file. When the log grows beyond that limit, iisnode module will truncate it.

Existing log files can either be appended to or created empty (log file names for processes with the same ordinal number are the same). This is controlled by the appendToExistingLog configuration setting. Lastly, the logFileFlushInterval setting controls how frequently the log file is flushed to disk.

Side by side with other content types

One of the more interesting benefits of hosting node.js applications in IIS using the iisnode module is support for a variety of content types within a single web site. Next to a node.js application one can host static HTLM files, client side JavaScript scripts, PHP scripts, ASP.NET applications, WCF services, and other types of content IIS supports. Just like the iisnode module handles node.js applications in a particular site, other content types will be handled by the registered IIS handlers.

Indicating which files within a web site are node.js applications and should be handled by the iisnode module is done by registring the iinode handler for those files in web.config. In the simplest form, one can register the iisnode module for a single *.js file in a web site using the ‘path’ attribute of the ‘add’ element of the handler collection:

<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</configuration>

Alternatively, one can decide that all files in a particular directory are supposed to be treated as node.js applications. A web.config using the <location> element can be used to achieve such configuration:

<configuration>
<location path="nodejsapps">
<system.webServer>
<handlers>
<add name="iisnode" path="*.js" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</location>
</configuration>

One other approach one can employ is to differentiate node.js applications from client side JavaScript scripts by assigning a file name extension to node.js applications other than *.js, e.g. *.njs. This allows a global iisnode handler registration that may apply across all  sites on a given machine, since the *.njs extension is unique:

<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="*.njs" verb="*" modules="iisnode" />
</handlers>
</system.webServer>
</configuration>

Minimal changes to existing HTTP node.js application code

It has been the aspiration for iisnode to not require extensive changes to existing, self-hosted node.js HTTP applications. To that end, most applications will only require a change in the specification of the listen address for the HTTP server, since that address is assigned by the IIS as opposed to left for the application to choose. The iisnode module will pass the listen address to the node.exe worker process in the PORT environment variable, and the application can read it from process.env.PORT:

var http = require('http');

http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('I am listening on ' + process.env.PORT);
}).listen(process.env.PORT);

If you access the endpoint created by the application above, you will notice the application is actually listening on a named pipe address. This is because the iisnode module uses HTTP over named pipes as a communication mechanism between the module and the worker node.exe process. One implication of this is HTTPS applications will need to be refactored to use HTTP instead of HTTPS. For those applications, HTTPS can be configured and managed at the IIS level itself.

Configuration

The iisnode module allows many of the configuration options to be adjusted with the system.webServer/iisnode section of web.config. Below is the list of options (most of which were described above) with their default values. For detailed description of the options check out the configuration sample.

<configuration>
<system.webServer>
<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>

Integrated management experience

The iisnode module configuration system is integrated with IIS configuration which allows common IIS management tools to be used to manipulate it. In particular, the appcmd.exe management tool that ships with IIS can be used to augment the iismodule configuration. For example, to set the maxProcessCountPerApplication value to 2 for the “Default Web Site/node” application, one can issue the following command:

%systemroot%\system32\inetsrv\appcmd.exe set config "Default Web Site/node" -section:iisnode /maxProcessCountPerApplication:2

This allows for scripting the configuration of node.js applications deployed to IIS.

Feedback

The iisnode project is in a very early stage as of this writing. I hope you will find it useful. Please report bugs, share ideas and experiences by leaving a comment here or through https://github.com/tjanczuk/iisnode/issues.

18 comments:

  1. Interesting stuff. Is it able to run something like express yet? Any big problems for using other existing node libraries?

    ReplyDelete
  2. What is the performance impact of this? Node can handle a lot of concurrent requests. I would think that putting an additional server in front of it would turn into a bottleneck.

    ReplyDelete
  3. We're looking at integrating this with Etherpad Lite! Nice work, keep an eye on the repo to see how we progress. Would be great to have your input if you get some spare time :)

    ReplyDelete
  4. This looks pretty useful!

    What I'd love to see:
    - An in-depth tutorial for those of us who recognize the value of the project but have no experience working with IIS
    - Benchmarks!

    ReplyDelete
  5. i heard that express works fine in node.exe

    ReplyDelete
  6. good work by the way, this is exciting.

    ReplyDelete
  7. I have several reports that express does not work with iisnode right now (although it does work with node.exe). I will be looking at the issue (as well as other key libraries). Supposedly it is due to the use of named pipes as a transport between iisnode and node.exe. Stay tuned.

    Performance benchmarks are planned and I will get the numbers out there when I know them.

    In terms of a tutorial and docs in general, stay tuned to https://github.com/tjanczuk/iisnode/wiki and my blog - this project is an evolving story. If you are passionate you can also help add content to https://github.com/tjanczuk/iisnode/wiki.

    ReplyDelete
  8. Scott Hanselman did a benchmark of iisnode using WCAT and his initial findings are here: http://www.hanselman.com/blog/InstallingAndRunningNodejsApplicationsWithinIISOnWindowsAreYouMad.aspx

    "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."

    ReplyDelete
  9. Is this a purely HTTP? Do you have any thoughts on running nodejs with socket.io/websockets in iisnode?

    -dk

    ReplyDelete
  10. What are the requirements on permissions/ACLs for the account running iisnode process?

    http://www.hanselman.com/blog/InstallingAndRunningNodejsApplicationsWithinIISOnWindowsAreYouMad.aspx?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+ScottHanselman+(Scott+Hanselman+-+ComputerZen.com)

    ReplyDelete
  11. iisnode is probably the most exciting development in the IIS world since ASP.NET appeared. We are witnessing history being made here. As a developer, I can hardly contain my excitement!

    ReplyDelete
  12. iisnode currently does not support websockets. It is currently limited to what IIS/HTTP.SYS can do. Once the IIS/HTTP.SYS stack supports websockets, iisnode will be adjusted to support it as well.

    The permission issue has been fixed as of v0.1.3. A fix is described at https://github.com/tjanczuk/iisnode/issues/28.

    ReplyDelete
  13. Express framework now works in iisnode. Walkthrough is here: http://tomasz.janczuk.org/2011/08/hosting-express-nodejs-applications-in.html

    ReplyDelete
  14. Excellent piece of development. Have you managed to get it working on Azure?

    ReplyDelete
  15. I followed the isntallation instructions but the only thing I can do calling the hello.js is actually downloading the file... I checked the module is registered in IIS. I am running on version 7.5. What can be the problem?

    ReplyDelete
  16. I have tried to install on my Intel 64 bit windows 7 machine. When I try to install the x86 version it instead points me to the amd64 bit install. When I install that, IIS just plain stops responding altogether. ????

    ReplyDelete
  17. The first sentence in this page mis-spelled the word "applications" as "appplications".

    ReplyDelete

About Me

My Photo
I am helping to develop and ship software for Microsoft. My current focus is node.js, JavaScript, and Azure. Before that I worked on .NET Framework and Silverlight, in particular web service technologies: Windows Communication Foundation (WCF), SOAP, WS-*, REST, AJAX.

Search This Blog

Loading...

Blog Archive