最近在搞用Client Script模擬壓力測試的把戲,順道對IE的MaxConnectionsPerServer屬性做了實測。

IE6/7預設會限制從同一台網站伺服器下載檔案最多只能同時用2條連線,換句話說,若一個網頁上有12個不同的<img>圖檔來自同一台網站伺服器,IE6/7並不是一口氣發出12個HTTP GET Request取回圖檔,而是讓12個Request排隊輪流共用兩條連線下載檔案。IE8起,同時連線數的上限才被提高到6條。(事實上,針對HTTP 1.0或1.1、寬頻或撥接環境,可使用的連線數上限並不相同,MSDN有篇文章提供了詳細的說明,有興趣的朋友可以一讀)

 

VersionHTTP 1.0 server (broadband connection)HTTP 1.1 server (broadband connection)HTTP 1.0 server (dial-up connection)HTTP 1.1 server (dial-up connection)
Internet Explorer 7 and earlier4242
Internet Explorer 86642

 

連線數愈多,允許多工下載明明是好事,莫非瀏覽器是怕效能太好,網頁顯示過快,使用者太開心心臟會受不了,基於健康考量才故意加上這個限制? 當然不是! 2條連線的建議源於HTTP 1.1協定,在HTTP 1.1起草的時代,要上網要靠Modem(數據機)撥接上線,你家Modem會透過電話線路怪吼怪叫跟遠方的Modem朋友聊天傳遞資料,在當時56kbps就已是極速,2條下載連線就差不多吃光頻寬,以其時空背景,這個建議挺合理的。不過,現在光纖網路動輒10M起跳,PC與伺服器的資料處理能力也不可同日而語,因應這個趨勢,IE8起才將連線數提高為6,針對頻寬與主機承載量更充裕的情境,微軟有篇KB: 我要如何將 Internet Explorer 設定為一次下載兩個以上的檔案? 提供了將連線數上限再提高到10的工具及方法。

在壓力測試的情境中,自然也需要將這個參數提高,為了驗證修改參數前後的效果,我寫了一個測試用的ASP.NET程式(檔名: MaxConnTest1.aspx):

排版顯示純文字
<%@ Page Language="C#" EnableSessionState="False" %>
 
<!DOCTYPE html>
<script runat="server">
    void Page_Init(object sender, EventArgs e)
    {
        if (Request["m"] == "ajax")
        {
            System.Threading.Thread.Sleep(200);
            Response.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
            Response.End();
        }
    }
</script>
<html>
<head runat="server">
    <title>MaxConnectionsPerServer Test</title>
    <script type="text/javascript" 
     src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.js"> </script>
    <script>
        $(function () {
            $("#disp").text(window.maxConnectionsPerServer);
            function ajaxCall(x, st) {
                $.get("MaxConnTest1.aspx", 
                    { m: "ajax", n: x, r: Math.random() },
                    function (r) {
                        $("body").append("<div>" + r + " -> " + x +
                        " (" + ((new Date).getTime() - st) + "ms)</div>");
                    });
            }
            for (var i = 0; i < 20; i++) 
                ajaxCall(i, (new Date).getTime());
        });
    </script>
</head>
<body>
    window.maxConnectionsPerServer = <span id='disp'></span>
</body>
</html>

這段程式會同時發出20個AJAX需求呼叫自己,由於是非同步XHR需求,20個Request會一次送出。伺服器端則在等待200ms後傳回精確度到ms的目前時間,延遲200ms是為了延長處理時間,故意製造出需排隊等候的條件,以形成同時連線並存的狀態。Client端程式在取得回應後會印出Server回傳的時間,Request的流水序號及該Request總執行時間。另外,IE8起提供的window.maxConnectionsPerServer屬性剛好可用來檢測目前允許的同時連線數上限。

在未修改MaxConnectionsPerServer設定前,執行結果為:

window.maxConnectionsPerServer = 6
17:33:30.785 -> 0 (250ms)
17:33:30.792 -> 1 (265ms)
17:33:30.805 -> 2 (281ms)
17:33:30.815 -> 3 (265ms)
17:33:30.815 -> 4 (280ms)
17:33:30.820 -> 5 (280ms)
17:33:31.027 -> 6 (468ms)
17:33:31.040 -> 7 (515ms)
17:33:31.040 -> 8 (515ms)
17:33:31.050 -> 10 (515ms)
17:33:31.057 -> 11 (515ms)
17:33:31.050 -> 9 (515ms)
17:33:31.257 -> 15 (1404ms)
17:33:31.315 -> 17 (1420ms)
17:33:31.317 -> 18 (1420ms)
17:33:31.315 -> 16 (1435ms)
17:33:31.317 -> 19 (1435ms)
17:33:31.317 -> 12 (1451ms)
17:33:32.210 -> 13 (1670ms)
17:33:32.255 -> 14 (1685ms)

由執行總時間,我們可以區分出0-5差不多為一批(<300ms),6-11為一批(450ms-520ms),15, 17, 18, 16, 19, 12約為一個梯次(14xx ms),13, 14則是最後一批(>1600 ms),每一批的數量就是六個,與maxConnectionsPerServer = 6不謀而合。透過HttpWatch的圖形顯示,可以看得更清楚! (最前面兩個分別是網頁本身及jquery的GET Request,可忽略,由第三項開始看起。另外下方有顏色說明,前方淺灰色的部分就是因無連線可用而被Block住的期間)

接著,修改Registry,在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_MAXCONNECTIONSPERSERVER新增iexplore.exe DWORD 0x10,等於允許16條同時連線。重新執行程式,再用HttpWatch觀察,可以明顯看出前16個Request沒有任何Blocked期間,最後4個才需要等待。

最後,我挑戰了FEATURE_MAXCONNECTIONSPERSERVER參數的上限128(設定超過128也只會到128),並將Request數也放大到128,由測試結果可以驗證的確做到了128個Request都沒有被Block,同時連線數真的達到128條,而Server端也因負荷量增加,花了3秒才消化回應完畢。

最後補充在本次測試中額外學到的知識:

  1. 在x64環境中,32位元版本IE的Registry位置在HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_MAXCONNECTIONSPERSERVER,若未加Wow6432Node,改的是64位元版IE的設定,我迷了好一陣子才驚醒到這點。
  2. ASP.NET宣告的EnableSessionState=”False”很重要,若沒加,會出現所有AJAX Request排成一列依序執行的現象,如以下的結果,234ms –> 437ms –> 672ms –> ... 每200ms傳回一個回應。ASP.NET Page執行過程中預設會獨家鎖定Session相關物件,因此同一時間只能有一個Thread執行,造成同一時間只能有一個Request被處理。以下是沒有關閉Session時的執行結果: (參考[Q: Does session state have a locking mechanism that serialize the access to state?])

window.maxConnectionsPerServer = 16
18:43:44.316 -> 0 (234ms)
18:43:44.535 -> 16 (437ms)
18:43:44.769 -> 17 (672ms)
18:43:45.003 -> 18 (906ms)
18:43:45.237 -> 19 (1140ms)
18:43:45.877 -> 1 (1779ms)
18:43:46.423 -> 3 (2341ms)
18:43:46.938 -> 2 (2840ms)
18:43:47.484 -> 4 (3402ms)
18:43:47.999 -> 5 (3917ms)
18:43:48.514 -> 6 (4479ms)
18:43:49.029 -> 8 (5010ms)
18:43:49.544 -> 10 (5462ms)
18:43:50.059 -> 11 (5962ms)
18:43:50.574 -> 7 (6508ms)
18:43:51.090 -> 9 (7007ms)
18:43:51.605 -> 12 (7538ms)
18:43:52.120 -> 13 (8007ms)
18:43:52.635 -> 14 (8522ms)
18:43:53.150 -> 15 (9037ms)

【警告】 調高MaxConnectionsPerServer有可能產生網路頻寬利用率下降、本機CPU負載過高、瀏覽器不穩等後遺症,建議調整時要留意其負面影響。


Comments

# by KKBruce

因為文章中提到[IE8 起],想再確認清楚一些,IE9 也有效嗎?[也許IE 10]

# by Jeffrey

to KKBruce, 以上實驗有一部分是在我自己的Windows 2008 R2 + IE9環境執行,IE9也是預設6條,透過同樣的Registry一樣可提高上限(但要留意文末所說的x64 Issue)。至於IE10,因尚在Preview階段,變化空間仍大,現階段暫不納入考量。

# by KKBruce

改了改了,不改對不起 IE (誤) 自己! ^_^ 該可以寫成 *.reg 直接讓人 Click,就完成修改才會方便。 如果大大沒空,我找個時間來寫。 再回報給你。

# by Jeffrey

to KKBruce,謝謝分享。其實沒做reg檔是不想電腦知識不夠深的人去更動它(修改registry算是個門檻),這個參數調得過大某些程度也像七傷拳,發出大量Request給Server的同時,對Client也會造成負擔。同時Handle過多的連線,我認為反而會降低網路頻寬的總體利用率,我擔心某些狀況外的人以為調大有百利而無一害,莫名其妙靠Reg檔就打開潘朶拉的盒子,在遇到電腦反應變慢或IE不穩定時卻又渾然不知是這個參數可能是問題根源,就等於種下一顆難抓的茶包,建議你不妨在文章上再加上調大可能會有負面影響的附註。

# by KKBruce

大大,我修改文章了。 你真的很利害,還需要多跟你學習。

# by KKBruce

在 2011/8/29之下載過檔案的各位大大,那個檔案有問題而且是錯誤的(還好不能使用),請重新下載,我分為Windows 7 x64/x32兩個版本了。

# by Eden

請問 MaxConnectionsPerServer 的數直有上限嗎?

# by Jeffrey

to Eden, 如文中所提,經實測FEATURE_MAXCONNECTIONSPERSERVER參數的上限為128,設定超過128也只會到128。

# by BenLu

請教一下,我們主機是2003 x64的電腦,他裡面的IE會有x86跟x64並存,我每次都使用預設的x64 IE去開啟網頁,可否在哪裡調整成用x32 IE去開啟網頁嗎?謝謝!

Post a comment