獲得實時更新的方法(Polling, Comet, Long Polling, WebSocket)
在 HTTP 協議上,只能由 Client 發起請求,等候 Server 端回應,然後獲得資料,來讓當前頁面更新。
但 Server 端是無法主動發起回應的,譬如說,當某討論串下面的留言更新了,要發起通知讓所有人的通知欄顯示一則未讀廣播,Server 端無法主動向所有正在線上的使用者進行廣播。
這時候就有幾種方法可以實作
Polling
早期的作法通常都是用 Javascript 來實作輪詢(Polling)的方式獲得 Server 端的最新資料。
利用 Javascript 中的 setInterval
或 setTimeout
在固定的時間內向 Server 端發起 Request 以 JSON 或 AJAX(xhr)的方式取得最新的資料。
備註: setTimeout
是較好的選擇,請參考 [javascript] 深入了解 setTimeout() 與 setInterval() 的不同之處
不過這麼做的好處是容易實現,也沒有其他瀏覽器的延伸問題,最主要的缺點就是造成資源的浪費以及負擔,因為你不知道 Server 端的資料何時會有更新,再沒有更新的情況下你一樣要發起 HTTP 請求,就會有浪費網路資源的狀況,如果是效能較差的裝置例如智慧型手機,可能就會有耗電、使用上卡頓的狀況發生。
Comet
Comet 如果要翻譯成中文是「彗星」的意思,他的作法是把發出的 Request 像彗星的尾巴一樣拉的很長(正常的 Request 很快就結束),這樣一來就可以讓伺服器保持連線的狀態,持續讓 Server 端 Response 資料回來,這樣的作法其實就是把 Polling 做在 Server 端。
但這樣的作法會把傳統的 Web Server 如 Apache 的連線給佔住,所以必須要配合 non-Blocking IO 的 Web Server 才能運作。
Long Polling
長時間輪詢的作法是 Comet 演化過後的方式,也是目前 Facebook、Plurk 實現動態更新的方法。
這樣的作法是發一個長時間等待的 Request,當 Server 端有資料 Response 時立刻斷掉、接著在發一個新的 Request。
與 Polling 不同之處的在於他比較有效率,可以等到 Timeout 或拿到新資料的時候在重新發送,相對之下減少很多網路資源的浪費。
加上通常是 Client 在傳遞資料(每次發 Request 時)以及沒有瀏覽器相容性的問題,算是比較常見的解法。
值得注意的是,如果你的 Server 端不支援 non-blocking IO 的話,他其實還是一般的 Polling
例如
(function polling() {
$.ajax({
url: "http://server",
type: "post",
dataType: "json",
timeout: 30000,
success: function(data) {
/* Do something */
},
complete: function() {
/* Polling here. */
polling();
}
});
})();
在上面的程式碼中,等待三十秒後重複發送 AJAX 請求到 Server 端,而 Long Polling 的作法是
- Client side 發送 Request 給 Server side
- Server 收到後 Response 給 Client ,並斷開連線
- Client 收到 Response 後,執行 Callback ,再次發送 Request 給 Sevrer
這樣的方式是無窮迴圈,但如果後端收到之後並沒有斷開連線,那麼前端就只會每 30 秒斷線重連,其實也就是一般的 Polling 而已。
非 non-blocking IO 的 Server 不行的原因:
假定送一個 AJAX 給 Server side, 事情做完之後,丟一個 Response 給 Client , 一樣會觸發 complete
條件。
但當你的 Server 沒放開連線,你只能等 Client timeout, 並且再次發送 Request 才能繼續動作,而這時候 Server 的資料到底有沒有完成,根本不知道,所以 non-blocking IO 的 Server 多少能避開這種問題。
但這都只是單向的溝通。
WebSocket
WebSocket 的誕生就是為了解決單向請求的問題,可以在一條連線上提供全雙工、雙向的資料傳輸,在這樣的標準下你可以很容易實作一個兼具可擴充性與即時性的網頁應用程式。
他的特點
- 支援程度高,參考Can I use?
- 建立於 TCP 協議之上,服務器端實現較容易
- 與 HTTP 協議有良好的兼容性,默認端口也是 80 與 443 port,並且在握手階段(handshake)採用 HTTP 協議,因此不容易被屏蔽,能通過各種 HTTP 代理服務器
- 性能較 Polling 好,通信高效
協議標示符號是 ws
(若加密則為 wss
),服務器網址就是 URL
ws://example.com:80/some/path
WebSocket 算是借用 HTTP 協議來完成一部分的 handshack
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
詳細更多部分請參考