上回測過SignalR四種傳輸方式的Server到Client段效能表現,確認Long Polling因不斷重發Request效率稍差,其餘兩種方式效能則相去不遠,WebSocket並無格外突出。先前剖析中,我們知道WebSocket最大特色在於"支援雙向傳輸",這回我們來個Server到Client、Client到Server傳輸各半的模擬情境。(WebSocket都做球給你了,好好表現囉~)

規劃以下測試情境: 每次Server送一個Runner到Client後(經由addRunner),Client必須用接收到的Runner資料呼叫Server端AckRunner()方式作為簽收,Server端在收到簽收回饋才算完成一次Round-Trip後,再送出下一筆Runner資料(經由AutoResetEvent實現收到回饋才送下一筆的同步邏輯),這個情境同時考驗接收與傳送兩個方向的效率。

Server端程式新增RoundTripTest()及AckRunner()兩個方法,在RoundTripTest()會在送出Runner後將其RoundTripSync(型別為AutoResetEvent) Reset(),接著WaitOne()等待它被Set();當Client端收到Runner後,呼叫該Server端的AckRunner()方法,其中會執行AutoResetEvent.Set(),使前述WaitOne()被放行,繼續執行下一筆。AckRunner()其實傳入Runner.Id即可,但我想讓Client到Server也傳送相同資料量,故選擇傳入整個Runner物件。

排版顯示純文字
        public void RoundTripTest()
        {
            var caller = Clients.Caller;
            Task.Factory.StartNew(() =>
            {
                foreach (var runner in dataStore.Values)
                {
                    runner.RoundTripSync.Reset();
                    caller.addRunner(runner);
                    runner.RoundTripSync.WaitOne();
                }
            });
        }
 
        public void AckRunner(Runner runner)
        {
            dataStore[runner.Id].RoundTripSync.Set();
        }

HTML端修改得不多,只在addRunner()中加入一行marathron.server.ackRunner(runner);

排版顯示純文字
            marathron.client.addRunner = function (runner) {
                runners.push(runner);
                $counter.text(runners.length + "@" + (new Date() - startTime) + "ms");
                marathron.server.ackRunner(runner);
                if (runner.Name == "Last")
                    $("#ulDisplay").append("<li>" + $counter.text() + "</li>");
            };

實測結果:

  • IE10 Forever Frame
    3756ms, 3670ms, 3719ms
  • IE10 Long Polling
    8764ms, 8280ms, 8510ms
  • IE10 WebSocket
    2539ms, 1850ms, 2808ms
  • Chrome Long Polling
    3651ms, 3853ms, 3572ms
  • Chrome Server Sent Event
    3245ms, 2995ms, 3167ms
  • Chrome WebSocket
    1575ms, 2215ms, 1123ms

【結論】

一如預期,支援雙向傳送的WebSocket免除1000次呼叫AckRunner()的(/signalr/send) Request,獲得壓倒性勝利! 快了近3倍。而Long Polling原本就不斷地在結束並重開Request,多了額外的1000次/signalr/send Request後,效能慘不忍睹。如此可推論,當Client呼叫Server端的次數愈頻繁,WebSocket就愈佔優勢,而Long Polling輸得愈慘,雖然用什麼傳輸方式取決於瀏覽器與伺服器的支援度,這個結果還是可做為不同情境效能表現的評估參考。

【補充】

SignalR決定傳輸方式的邏輯如下: (參考)

  1. IE6/7/8? 直接保送Long Polling (這又給了我們一個不該用老IE的好理由)
    【2013-12-06更新】依據官方文件,SignalR 2.0僅支援IE8+,感謝ChrisTorng補充
  2. 若啟動連線時指定了JSONP參數 –> Long Polling
  3. 如果SignlaR連線對象為跨網域,且滿足以下情境,將採WebSocket,否則用Long Polling
    * Client支援CORS
    * Client支援WebSocket
    * Server支援WebSocket
  4. 若未指定JSONP參數且Server/Client都支援WebSocket –> WebSocket
  5. 若Client或Server端不支援WebSocket,但支援Server Sent Event –> Server Sent Event
  6. 如果前述Server Sent Event不可用,改用Forever Frame
  7. 如果前述Forever Frame也失敗,改用Long Polling

Comments

# by ChrisTorng

我有看到原文的 If the browser is Internet Explorer 8 or earlier, Long Polling is used。 不過在 Supported Platforms http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/supported-platforms 也有明確列出,僅支援 IE8 以上... 更精確地說,在 our testing matrix http://testswarm.signalr.net/user/signalr 內,最新的 496edbde69 http://testswarm.signalr.net/job/556 中,列出支援 IE8 搭配 jQuery 1.6.4/jQuery 1.9.1,但不支援 jQuery 2.0.0,而 IE9 則支援上述三個 jQuery 版本。

# by Jeffrey

to ChrisTorng, 謝謝補充,已加入本文。

# by SAM

請問黑大可否讓小弟我轉在FB上?感謝萬分,辛苦您了。

# by Jeffrey

to SAM, 歡迎轉發,註明出處即可。

Post a comment