IIS8 RewriteModule / URLRewrite Extremely Slow

2019-08-19 01:24发布

问题:

I'm running a website with IIS8 on Windows Server 2012. I'm trying to determine what's causing high CPU usage by IIS (frequently 50% or more CPU usage by IIS). Server receives about 40 total requests per second throughout the day, but probably only 1-2 URLs per second that require processing.

I enabled Request Tracing and found that some RewriteModule requests are taking over 100 seconds (!) to complete. I'm having trouble determining how this is possible on a machine with more than sufficient hardware. The exact same URL structure is processed via mod_rewrite on Apache in less than a second.

An example URL would be like this:

http://<domain-name>/Product/<Parent-Category>/<Child-Category1>/<Child-Category2>/<Child-Category3>/<Product-Name>

The accompanying rewrite rule is:

<rule name="Rule" stopProcessing="true">
      <match url="^Product/([^/\?]+)/?([^/\?]+)?/?([^/\?]+)?/?([^/\?]+)?/?([^/\?]+)?/?([^/\?]+)?/?[\?]?(.+)?$"/>
      <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
      </conditions>
      <action type="Rewrite" url="/Product/index.php?a={R:1}&amp;b={R:2}&amp;c={R:3}&amp;d={R:4}&amp;e={R:5}&amp;f={R:6}&amp;{R:7}" appendQueryString="true"/>
    </rule>

Is there something in the way that I'm defining the match URL that's causing high processing time? Some of the matching urls contain a high number of characters if they utilize many parent/child categories (up to 5, typically 3-4).

回答1:

Problem is definitely in your regexp. Best way is try to split it to different more specific patterns.

If it is not the case, this rule should keep the same functionality and work faster:

<rule name="Rule" stopProcessing="true">
  <match url="^Product/([^/]+)/?([^/]+)?/?([^/]+)?/?([^/]+)?/?([^/]+)?/?([^/]+)?"/>
  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  </conditions>
  <action type="Rewrite" url="/Product/index.php?a={R:1}&amp;b={R:2}&amp;c={R:3}&amp;d={R:4}&amp;e={R:5}&amp;f={R:6}" appendQueryString="true"/>
</rule>

I removed unnecessary \? checks in your regexp, because patter inside <match url is checking URL without query string, so, it is redundant to have ? checks in your regexp.

I checked on my PC and it works definitely faster, but you need to double check that it remains the same functionality



回答2:

I did extensive testing and changed the rule to the following. This resulted in CPU dropping to 1% and the previous 100-second completion time dropping to roughly 50ms.

I still don't understand how this is possible - this is an 4 CPU/8-core machine with 48GB of RAM, and IIS8 took 100 seconds to pull apart a 70 character string and compare it to the previous regex. Unless the previous example somehow created a near infinite loop, I don't see how it could possibly be that slow.

New Rule:

<rule name="Rule" stopProcessing="true">
      <match url="^Product/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)?[/]?(.+)?$"/>
      <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
      </conditions>
      <action type="Rewrite" url="/Product/index.php?a={R:1}&amp;b={R:2}&amp;c={R:3}&amp;d={R:4}&amp;e={R:5}&amp;f={R:6}&amp;{R:7}" appendQueryString="true"/>
    </rule>