原文:https://www.jianshu.com/p/0e3654d14cf6
場景
假設我們現在要實作這樣一個功能:浏覽器要實時展示服務端計算出來的資料。
一種可能的實作是:(短輪詢)浏覽器頻繁(例如定時1秒)向服務端發起請求以獲得服務端資料。但定時請求并不能“實時”反應服務端的資料變化情況。
若定時周期為S,則資料延遲周期最大即為S。若想縮短資料延遲周期,則應使S盡量小,而S越小,浏覽器向服務端發起請求的頻率越高,又造成網絡握手次數越多,影響了效率
綜上:短輪詢兩個缺點。1、定時輪詢,占用伺服器帶寬,記憶體占用,造成資源浪費。2、不能盡可能實時反應資料變化,主動拉的模式還是存在一定延遲
DeferredResult解決方案:
基于請求-響應機制,隻不過發起的請求會在服務端挂起,直到請求逾時或服務端有資料推送時才會做出響應,響應的時機完全由服務端控制。是以,整體效果看起來就像是服務端真的在“實時推送”一樣。
可以利用SpringMVC的DeferredResult來實作異步長連接配接的服務端實時推送。
使用DeferredResult實際上是推模式(不是真正的推模式),可以使用長連接配接+DeferredResult,實作服務端實時響應。實作盡可能的減少服務端資源浪費和占用,實時資料推送,延遲低
入門
後端代碼
@RequestMapping("/call")
@ResponseBody
public DeferredResult<Object> call() { // 泛型Object表示傳回結果的類型
DeferredResult<Object> response = new DeferredResult<Object>(
10000, // 請求的逾時時間
null); // 逾時後響應的結果
response.onCompletion(new Runnable() {
@Override
public void run() {
// 請求處理完成後所做的一些工作
}
});
// 設定響應結果
// 調用此方法時立即向浏覽器發出響應;未調用時請求被挂起
response.setResult(new Object());
return response;
}
前端代碼
var loopCall = function() {
$.get("${yourContext}/call", function(r) {
loopCall();
console.log("call: ");
console.log(r);
});
};
loopCall(); // 循環發起異步請求
執行流程
執行邏輯
1、浏覽器發起異步請求
2、請求到達服務端被挂起(使用浏覽器檢視請求狀态,此時為pending)
3、向浏覽器進行響應,分為兩種情況:
- 調用DeferredResult.setResult(),請求被喚醒,傳回結果
- 逾時,傳回一個你設定的結果
4、浏覽得到響應,再次重複1,處理此次響應結果