上回測過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物件。
publicvoid RoundTripTest()
{
var caller = Clients.Caller;
Task.Factory.StartNew(() =>
{
foreach (var runner in dataStore.Values)
{
runner.RoundTripSync.Reset();
caller.addRunner(runner);
runner.RoundTripSync.WaitOne();
}
});
}
publicvoid 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決定傳輸方式的邏輯如下: (參考)
- IE6/7/8? 直接保送Long Polling (這又給了我們一個不該用老IE的好理由)
- 若啟動連線時指定了JSONP參數 –> Long Polling
- 如果SignlaR連線對象為跨網域,且滿足以下情境,將採WebSocket,否則用Long Polling
* Client支援CORS
* Client支援WebSocket
* Server支援WebSocket - 若未指定JSONP參數且Server/Client都支援WebSocket –> WebSocket
- 若Client或Server端不支援WebSocket,但支援Server Sent Event –> Server Sent Event
- 如果前述Server Sent Event不可用,改用Forever Frame
- 如果前述Forever Frame也失敗,改用Long Polling