天天看點

DeferredResult的使用場景及用法

原文: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,處理此次響應結果