I recently rolled out an enterprise wide app to a pilot group (which, since pilots are silly or so I’m told, that means we rolled it out to EVERYONE or ~1200 users). Not a huge amount by any means, but without a pilot, we didn’t have a good baseline to gauge performance settings.
I remember reading an article by Peter Bromberg ages ago about tuning IIS 6.0. After a bit of Googling, I found it again and, one-by-one, started tweaking various settings on our development environment and hitting it with 500–1000 concurrent load users.
Here’s what I came up with that seemed to give the best bang for the buck.
<system.net>
<connectionManagement>
<add address=”*” maxconnection=”100″ />
</connectionManagement>
</system.net>
maxconnections – This sets how many outgoing connections the ASP.NET service can make FROM the server, such as to databases, web services, etc. Increasing this can increase the number of concurrent connections are available to your application.
<system.web>
<deployment retail=”true” />
<processModel
autoConfig=”true”
memoryLimit=”75″
maxIoThreads=”100″
minIoThreads=”30″
minWorkerThreads=”40″
maxWorkerThreads=”100″
clientConnectedCheck=”00:00:05″ />
<httpRuntime
minFreeThreads=”352″
minLocalRequestFreeThreads=”308″
enableKernelOutputCache=”false”
maxRequestLength=”10240″ />
</system.web>
deployment/retail – Setting retail=”true” essentially forces all web applications on that machine to compile without debug. This is extremely helpful for those rare cases that you forget to set debug=”false” on a web project. In a production environment, debug files should rarely/never be needed, so this blankets the server.
processModel/autoConfig – autoConfig sets everything to the defaults except what I explicitly set below. Just at time saver.
processModel/memoryLimit – This is a percentage of how much memory a web process can consume before it splits off into another process. Since our production servers ONLY run IIS and web servers, then it should be safe to set this to 75% of the total memory on the box.
processModel/maxIoThreads – Based on Bromberg’s recommendation, this controls how many IO threads are allowed to the web process. minIoThreads is the other side of the scale.
processModel/maxWorkerThreads – Based on Bromberg’s recommendation, this controls how many worker threads are in the pool for each web process. minWorkerThreads is the other side of the scale.
processModel/clientConnectedCheck – An excellent setting that tells the server to check every 5 seconds to see if the client is connected. If not, trash their queued requests (since they’re not there to receive it anymore). According to this source, this also helps for those situations where users get “impatient” and machine-gun click the mouse trying to get a response.
httpRuntime/minFreeThreads – Based on Bromberg’s recommendation, a setting that tells the machine what the minimum number of threads in the pool must be in order for incoming requests to be processed.
httpRuntime/minLocalRequestFreeThreads – Same as the prior, but for the local machine (localhost)—good for locally hosted web servers; however, I’m not sure if it’s smart enough to know if “localhost” and a registered DNS entry are the same (I never refer to a machine as localhost). It’s set, but more research required here.
httpRuntime/enableKernelOutputCache – This is set to FALSE for me, though most everything recommends true. Why? Kernel Mode caching is great—it’s speedy and a good way to speed up pages; however, we’ve had several issues in the past (on IIS 6) where it caused sessions to “merge” across authenticated users. The issue is described here in KB917072. Setting this for the machine just solves the problem overall. Honestly, unless I can guarantee a way that the sessions/cookies won’t cross, I’ll give up a few milliseconds of performance in the day to guarantee a user experience.
httpRuntime/maxRequestLength – The default request length is 4096K; however, for uploading large file into the system, that becomes the limit. A few of our applications allows users to upload documents, PDFs, etc. and attach them—we need a larger limit. ~10MB seems to work well. You “could” set this in the web.config; however, we base our production server at 10MB and then ramp up from there.
As with all tweaks, your mileage may vary—as mine does every single day. I’d be interested to hear of other tweaks for higher performing web sites as well as feedback on the above (especially if I misinterperted a setting and simply haven’t seen the failure yet).