博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DeferredResult使用方式和场景
阅读量:5098 次
发布时间:2019-06-13

本文共 4923 字,大约阅读时间需要 16 分钟。

为什么使用DeferredResult?

当一个请求到达API接口,如果该API接口的return返回值是DeferredResult,在没有超时或者DeferredResult对象设置setResult时,接口不会返回,但是Servlet容器线程会结束,DeferredResult另起线程来进行结果处理(即这种操作提升了服务短时间的吞吐能力),并setResult,如此以来这个请求不会占用服务连接池太久,如果超时或设置setResult,接口会立即返回

使用DeferredResult的流程:

    1. 浏览器发起异步请求
    2. 请求到达服务端被挂起
    3. 向浏览器进行响应,分为两种情况:
      3.1 调用DeferredResult.setResult(),请求被唤醒,返回结果
      3.2 超时,返回一个你设定的结果
    4. 浏览得到响应,再次重复1,处理此次响应结果

 

给人一种异步处理业务,但是却同步返回的感觉。

场景

浏览器向A系统发起请求,A系统需要等到B系统给A推送数据时,A才会立刻向浏览器返回数据。

 

Demo代码

接口代码:

/get是调用A系统的接口返回数据;

/result模拟B系统向A推送数据进行setResult。

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.context.request.async.DeferredResult;@RestController@RequestMapping(value = "/deferred-result")public class DeferredResultController {    @Autowired    private DeferredResultService deferredResultService;    /**     * 为了方便测试,简单模拟一个     * 多个请求用同一个requestId会出问题     */    private final String requestId = "haha";    @GetMapping(value = "/get")    public DeferredResult
get(@RequestParam(value = "timeout", required = false, defaultValue = "10000") Long timeout) { DeferredResult
deferredResult = new DeferredResult<>(timeout); deferredResultService.process(requestId, deferredResult); return deferredResult; } /** * 设置DeferredResult对象的result属性,模拟异步操作 * @param desired * @return */ @GetMapping(value = "/result") public String settingResult(@RequestParam(value = "desired", required = false, defaultValue = "成功") String desired) { DeferredResultResponse deferredResultResponse = new DeferredResultResponse(); if (DeferredResultResponse.Msg.SUCCESS.getDesc().equals(desired)){ deferredResultResponse.setCode(HttpStatus.OK.value()); deferredResultResponse.setMsg(desired); }else{ deferredResultResponse.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); deferredResultResponse.setMsg(DeferredResultResponse.Msg.FAILED.getDesc()); } deferredResultService.settingResult(requestId, deferredResultResponse); return "Done"; }}

 

import org.springframework.http.HttpStatus;import org.springframework.stereotype.Service;import org.springframework.web.context.request.async.DeferredResult;import java.util.Map;import java.util.Optional;import java.util.concurrent.ConcurrentHashMap;import java.util.function.Consumer;@Servicepublic class DeferredResultService {    private Map
> taskMap; public DeferredResultService() { taskMap = new ConcurrentHashMap<>(); } /** * 将请求id与setResult映射 * * @param requestId * @param deferredResult */ public void process(String requestId, DeferredResult
deferredResult) { // 请求超时的回调函数 deferredResult.onTimeout(() -> { taskMap.remove(requestId); DeferredResultResponse deferredResultResponse = new DeferredResultResponse(); deferredResultResponse.setCode(HttpStatus.REQUEST_TIMEOUT.value()); deferredResultResponse.setMsg(DeferredResultResponse.Msg.TIMEOUT.getDesc()); deferredResult.setResult(deferredResultResponse); }); Optional.ofNullable(taskMap) .filter(t -> !t.containsKey(requestId)) .orElseThrow(() -> new IllegalArgumentException(String.format("requestId=%s is existing", requestId))); // 这里的Consumer,就相当于是传入的DeferredResult对象的地址 // 所以下面settingResult方法中"taskMap.get(requestId)"就是Controller层创建的对象 taskMap.putIfAbsent(requestId, deferredResult::setResult); } /** * 这里相当于异步的操作方法 * 设置DeferredResult对象的setResult方法 * * @param requestId * @param deferredResultResponse */ public void settingResult(String requestId, DeferredResultResponse deferredResultResponse) { if (taskMap.containsKey(requestId)) { Consumer
deferredResultResponseConsumer = taskMap.get(requestId); // 这里相当于DeferredResult对象的setResult方法 deferredResultResponseConsumer.accept(deferredResultResponse); taskMap.remove(requestId); } }}

 

import lombok.Data;import lombok.Getter;@Datapublic class DeferredResultResponse {    private Integer code;    private String msg;    public enum Msg {        TIMEOUT("超时"),        FAILED("失败"),        SUCCESS("成功");        @Getter        private String desc;        Msg(String desc) {            this.desc = desc;        }    }}

 

测试

1. 超时

设置超时时间为8s,会一直阻塞8s,如果超时,返回默认超时的结果

 8s过去,没有setResult,返回超时

2. 进行setResult

在阻塞期间调用setResult,我这里模拟的是B系统推送数据给A时抛异常失败的情况,会立刻得到返回结果

 

 

 

 

参考:

 

转载于:https://www.cnblogs.com/theRhyme/p/10846349.html

你可能感兴趣的文章
HtmlUnitDriver 网页内容动态抓取
查看>>
ad logon hour
查看>>
获得进程可执行文件的路径: GetModuleFileNameEx, GetProcessImageFileName, QueryFullProcessImageName...
查看>>
证件照(1寸2寸)拍摄处理知识汇总
查看>>
罗马数字与阿拉伯数字转换
查看>>
Eclipse 反编译之 JadClipse
查看>>
asp.net 获取IP地理位置的几个主要接口
查看>>
Python入门-函数
查看>>
[HDU5727]Necklace(二分图最大匹配,枚举)
查看>>
距离公式汇总以及Python实现
查看>>
设计模式之装饰者模式
查看>>
一道不知道哪里来的容斥题
查看>>
Blender Python UV 学习
查看>>
window添加右键菜单
查看>>
入手腾龙SP AF90mm MACRO
查看>>
python学习4 常用内置模块
查看>>
Window7上搭建symfony开发环境(PEAR)
查看>>
ResolveUrl的用法
查看>>
Linux内核态、用户态简介与IntelCPU特权级别--Ring0-3
查看>>
第23月第24天 git命令 .git-credentials git rm --cached git stash clear
查看>>