Web服務器主動推送技術


HTTP協議遵循經典的客戶端-服務器模型,客戶端發送一個請求,然后等待服務器端的響應,服務器端只能在接收到客戶端的請求之后進行響應,不能主動的發送數據到客戶端。

客戶端想要在不刷新頁面的情況下實時獲取到服務器端最新的數據,可以通過以下途徑:

  1. 輪詢
  2. 長輪詢
  3. HTTP流
  4. SSE
  5. Web Sockets

1. 輪詢

客戶端(瀏覽器)定時向服務器端發送請求,獲取最新的數據。可以通過在一個定時器中觸發ajax請求來實現:

//每兩秒觸發一次ajax請求,獲取最新的數據
setInterval(function(){
   //do some ajax call here to retrieve latest data
},2000);
 

優點:

實現簡單,JS端進行一些更改即可,無需后端服務任何改動

缺點:

輪詢的間隔過長,會導致用戶不能及時接收到更新的數據;輪詢的間隔過短,會導致查詢請求過多,增加服務器端的負擔

2. 長輪詢

長輪詢方法實現原理如下:

  1. 客戶端發起一個請求到服務器端(http request)
  2. 服務器端一直保持連接打開,直到有數據數據可發送給客戶端,再返回這個請求(http response)
  3. 客戶端收到服務器端返回的數據后,處理數據,並立馬發起一個新的請求
  4. ...
 
//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會周期性的變成3responseText包含所有的數據源。通過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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM