天天看点

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,处理此次响应结果