文章出處

萬萬沒有想到!當初為了解決使用負載均衡時記錄客戶端IP地址的問題,在IIS URL Rewrite Module中增加了一條URL重寫規則(詳見遷入阿里云后遇到的Request.UserHostAddress記錄IP地址問題):

<rewrite>
    <allowedServerVariables>
        <add name="REMOTE_ADDR" />
    </allowedServerVariables>
    <globalRules>
        <rule name="HTTP_X_Forwarded_For-to-REMOTE_ADDR" enabled="true">
            <match url=".*" />
            <serverVariables>
                <set name="REMOTE_ADDR" value="{HTTP_X_Forwarded_For}" />
            </serverVariables>
            <action type="None" />
            <conditions>
                <add input="{HTTP_X_Forwarded_For}" pattern="^$" negate="true" />
            </conditions>
        </rule>
    </globalRules>
</rewrite>

這竟然造成http.sys的內核模式緩存(kernel mode caching)被IIS URL Rewrite Module禁用,禁用理由是重寫規則中用到了影響緩存安全的服務器變量(cache unsafe server variable)——{HTTP_X_forwarded_For}。

URL重寫竟然能影響到處于內核模式的http.sys,誰能想到?微軟想到了,而且做到了!

當知道這個真相后,真的很惱火!平時誰會注意http.sys的緩存是否正常工作,如果不是因為最近在解決“黑色1秒”問題,估計再過十年也不會發現。

那我們是怎么發現的呢?借助于Windows性能監視器(Performance Monitor)。

針對http.sys kernel mode cache的性能監視器

在添加了HTTP Service的三個監測項目——TotalUrisCached, UriCachedHits, UriCacheMisses之后發現,TotalUrisCached與UriCachedHits值一直是0,而UriCacheMisses的值巨大無比,一看就知道kernel mode caching出問題了。

后來發現一個命令可以更輕松地進行檢測:

netsh http show cachestate

如果出現上圖的畫面,說明kernel mode caching沒干活。

當我們禁用了讓kernel mode caching罷工的URL重寫規則后,用瀏覽器訪問一個網址,然后Ctrl+F5刷新2次(10秒內被訪問2次就會被http.sys緩存),然后運行命令netsh http show cachestate:

netsh http show cachestate

從上圖中可以看出請求的內容被http.sys成功緩存了。

那內核模式緩存失效會帶來什么影響呢?

誰都知道這會影響了網站的處理性能,而對我們來說還有一個重大影響——在“黑色1秒”問題的排查過程中,它讓我們作出了錯誤的判斷,以為“黑色1秒”期間http.sys進程卡住了(依據緩存沒工作),詳見“黑色30秒”走了,“黑色1秒”來了,真相也許大白了。現在看來,如果解決了http.sys緩存問題,“黑色1秒”期間IIS日志中很可能有緩存輸出的記錄,如果真是這樣,那引發“黑色1秒”的環節可能在WAS(Windows Process Activation Service),這將把我們帶向不同的問題排查方向。

那如何解決這個問題呢?

目前只想到兩個方式:

1. 棄用IIS URL Rewrite Module,但目前未找到更好的選擇。

2. 讓阿里云修改SLB的轉發規則,將http headers中的REMOTE_ADDR修改為真實的客戶端IP。

3. 修改代碼,不通過Request.UserHostAddress獲取IP。

【最終選擇的解決方法】

寫了兩個擴展方法:

namespace System.Web
{
    public static class HttpRequestExtension
    {
        //針對WebForm
        public static string GetUserIp(this HttpRequest request)
        {
            var ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if(string.IsNullOrEmpty(ip))
            {
                ip = request.UserHostAddress;
            }
            return ip;
        }

        //針對MVC
        public static string GetUserIp(this HttpRequestBase request)
        {
            var ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (string.IsNullOrEmpty(ip))
            {
                ip = request.UserHostAddress;
            }
            return ip;
        }
    }
}

然后將代碼中所有調用Request.UserHostAddress的地方改為調用擴展方法Request.GetUserIp()。

【參考資料】

URL Rewrite Module 1.1 for IIS 7

Working with HTTP.SYS or Kernel Mode Caching in Internet Information Services 6.0

URL Rewrite Module Configuration Reference


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()