IISNode Performance Debugging and Optimization
Unfortunately, information is scarce on IISNode deployment tactics, and the original developer is no longer with Microsoft (or the project); however, the project is still being maintained by the Azure team since IISNode runs Azure's Node.JS web application instances.
IISNode can be slow and ill-performant at times, but there are some key tweaks you can make to your configuration (beyond standard Node.JS performance tweaks) that will speed things up.
The first thing we need to look at is the IISNode configuration itself. IISNode relies on configuration elements within the web.config file in the root of your application, but it can be overridden with an issnode.yml file--what you use depends on how you like to configure things.
Here's an example web.config entry optimized for performance (and debugging):
<iisnode nodeProcessCommandLine="C:\Program Files\nodejs\node.exe --no-deprecation --no-warnings" promoteServerVars="HTTP_UID,HTTP_PUBCOOKIE_USER,LOGON_USER,HTTP_SHIBSESSIONID" node_env="production" nodeProcessCountPerApplication="8" debugHeaderEnabled="true" devErrorsEnabled="true" />
I have two debug and development values set that you can ignore. The configuration items that directly affect performance are the
node_env attribute and the
nodeProcessCountPerApplication attribute. Most will be familiar with
node_env, and will likely already have that set to production. For
nodeProcessCountPerApplication, this indicates the number of cores used. Since Node.JS is a single event loop, it only uses a single processor. IISNode allows you to spawn multiple processes in order to load balance between cores. Setting this value to "0" (zero) will spawn one process per core, but sometimes with virtual cores, it's been known to spawn one extra.
You can also adjust the
maxConcurrentRequestsPerProcess. The default is
1024. Out of the box, a deployed IISNode application will default to a single core with max concurrent requests set at 1024. That's not a lot of concurrent requests if you have a bad configuration. Be sure to adjust appropriately.
maxConcurrentRequestsPerProcess, and is a lot slower than just having IIS handle static files. You can fix this by using the built-in static file module along with a rewrite rule.
First the rewrite rule:
<rule name="StaticContent" stopProcessing="true"> <match url="([\S]+[.](jpg|jpeg|gif|css|png|js|ts|cscc|less|ico|html|map|svg))" /> <action type="None" /> </rule>
Now some static file handling:
<add name="StaticFilesCss" path="*.css" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" /> <add name="StaticFilesPng" path="*.png" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
The above would be added to the
<handlers/> node in the web.config, and you'll want to put them before the IISNode handler. Also, the above example is just rewriting CSS and PNG files. You'll want to adjust your static file serving to meet your needs.
Another thing you can do is alter the client content cache directives by putting the following code in your
<staticContent> <clientCache cacheControlMode="UseMaxAge" /> </staticContent>
This will default the client cache to the max age, allowing the client's browser to do some heavy lifting.
The configuration options above should improve your performance exponentially, but if you are still having problems, check your event loop blocking calls. In particular, check your database connections.
If you're deploying to IIS on a Windows machine, are you connecting to SQL Server? If so, you're likely using the "node-msssql" package that sits on top of Tedious.
One of the confusing aspects of "node-mssql" is that documentation is up-in-the-air about the best way to handle connections and connection pools. One thing is for certain: Don't close your connection pools yourself. Let the module (and SQL Server) handle that for you. Manually closing the connection pools can decrease your performance significantly.
In fact, while working on performance tuning an application, I noticed the connection pools were being closed after the database queries were being executed. Just by removing the calls to
pool.close() I was able to move from 400 requests in 10 seconds to 4,000 requests in 10 seconds.
These are just a handful of quick tips, and there are other ways you can improve performance as well, such as introducing application caches (whether "node-cache" or Redis), but first always tune IISNode.