HTTP協議遵循經典的客戶端-服務器模型,客戶端發送一個請求,然后等待服務器端的響應,服務器端只能在接收到客戶端的請求之后進行響應,不能主動的發送數據到客戶端。
客戶端想要在不刷新頁面的情況下實時獲取到服務器端最新的數據,可以通過以下途徑:
- 輪詢
- 長輪詢
- HTTP流
- SSE
- Web Sockets
1. 輪詢
客戶端(瀏覽器)定時向服務器端發送請求,獲取最新的數據。可以通過在一個定時器中觸發ajax請求來實現:
//每兩秒觸發一次ajax請求,獲取最新的數據
setInterval(function(){
//do some ajax call here to retrieve latest data
},2000);

優點:
實現簡單,JS端進行一些更改即可,無需后端服務任何改動
缺點:
輪詢的間隔過長,會導致用戶不能及時接收到更新的數據;輪詢的間隔過短,會導致查詢請求過多,增加服務器端的負擔
2. 長輪詢
長輪詢方法實現原理如下:
- 客戶端發起一個請求到服務器端(http request)
- 服務器端一直保持連接打開,直到有數據數據可發送給客戶端,再返回這個請求(http response)
- 客戶端收到服務器端返回的數據后,處理數據,並立馬發起一個新的請求
- ...

//server端示例(nodejs)
var aTargets = [];
app.get('/notification', function(req, res) {
aTargets.push(res);
//res.end(); 這里不調用res.end(),讓http request連接一直存活着
})
//此方法會在有新的數據時調用
onNewNotification : function (data) {
aTargets.forEach(function(res){
res.send(data);//當有新的數據時,再調用res.send(data)返回最新的數據,結束一次http請求
})
}
優點:
- 可以及時獲取到最新的數據
- 相較於輪詢策略,減少了請求數量
缺點:
服務器端要一直保持連接,不能釋放,由於一個服務器能夠處理的連接數有限,當達到服務器處理的上限的時候,服務器將無法響應新的請求
3. HTTP流
HTTP流區別於輪詢和長輪詢方法,它在客戶端網頁的生命周期內,只需要使用一個HTTP連接,也就是只會向服務器發送一個請求,對於這個請求,服務器會保持HTTP連接(不返回response),然后周期性的向瀏覽器發送數據。
//server端示例(nodejs)
let express = require("express");
let app = express();
app.use(express.static("resources"));
app.get("/httpstream",function(req, res){
var x = 0;
res.setHeader('Connection', 'Transfer-Encoding');
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Transfer-Encoding', 'chunked');//聲明數據傳輸編碼為chunked,讓瀏覽器及時處理
setInterval(function(){
res.write(x+++"|"); //每隔2s向客戶端發送一次數據
},2000);
});
app.listen(3000);
服務器端接收到請求后,每隔兩秒向客戶端輸出一點文字,但是不會使用res.end()
或者res.send()
結束當前http請求。
//客戶端示例js
var xhr = new XMLHttpRequest();
var received = 0;
var result = "";
xhr.open("get","/httpstream",true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 3) { //readystate 3 表示正在解析數據
result = xhr.responseText.substring(received);//截取最新的數據
received += result.length;
console.log(result);
}
}
xhr.send();
隨着不斷從服務器端接收到數據,客戶端的readyState
會周期性的變成3
,responseText
包含所有的數據源。通過received
來記錄之前已經處理過的數據長度,然后在responseText
中截取最新的數據。

優點:
頁面的整個生命周期內,只需要建立一個http連接
缺點:
- 如果接入的客戶端過多,服務器端會因為http連接有限而無法為新的客戶端提供服務
- 客戶端接收到的數據流會越來越大,最終可能會引發頁面的性能問題
4. SSE
SSE(Server-Sent Events)是基於HTTP實現的一套服務器向客戶端發送數據的API。他是針對上面說到的三種方法(輪詢,長輪詢,HTTP流)的一個標准API實現。
使用SSE API可以創建到服務器端的但相連接,服務器可以通過這個連接發送任意數據。它有以下特點:
- 斷開自動連接
- 服務器響應的MIME類型必須是
text/event-stream
- 需要瀏覽器API支持(參考瀏覽器兼容性)
使用方法如下:
//客戶端js
var source = new EventSource(url);
//建立連接時觸發
source.onopen = function () {
//do something here
};
//從服務器端接收到新的事件時觸發
source.onmessage = function (event) {
var data = event.data; //服務器返回的數據存放在event.data中
};
//連接異常時觸發
source.onerror = function () {
//do something here
};
客戶端創建一個EventSource對象,綁定到對應的url,然后監聽該對象的onmessage事件就可以獲取到最新的數據。
//server端示例(nodejs)
let express = require("express");
let app = express();
app.use(express.static("resources"));
app.get("/httpstream",function(req, res){
var x = 0;
res.writeHead(200, {
"Content-Type":"text/event-stream",
"Cache-Control":"no-cache",
"Connection":"keep-alive"
});
//每個1s往客戶端發送一條數據
setInterval(function(){
res.write("data: " + x++ + "\n\n");//發送的數據格式必須是"data: <內容>/n/n"
},1000);
});
app.listen(3000);
5. Web Sockets
不同於SSE,Web Sockets 采用了一套全新的協議(ws/wss
)來建立客戶端到服務器端的全雙工、雙向通信連接。
關於web sockets的使用,這篇文章:http://www.ruanyifeng.com/blog/2017/05/websocket.html 已經介紹的非常全面了,我就不再贅述。
優點:
- 雙向通信,實時連接
- 相較於HTTP請求更加高效(不需要握手,連接始終存在;無需攜帶頭部信息)
缺點:
- 穩定性和成熟度問題
建議:
在使用的過程中,請根據產品將來的使用環境(支持的瀏覽器類型、版本),使用場景(雙向通信、單向通信)這些點,並結合每一種方法的優缺點去考慮,然后選取對應的策略。
******************轉摘:https://www.jianshu.com/p/72372741df5f