返回顶部
首页 > 资讯 > 后端开发 > Python >详解Spring DeferredResult异步操作使用场景
  • 176
分享到

详解Spring DeferredResult异步操作使用场景

2024-04-02 19:04:59 176人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录为什么使用DeferredResult?场景Demo代码测试1. 超时2. 进行setResult总结:为什么使用DeferredResult? api接口需要在指定时间内将异步

为什么使用DeferredResult?

api接口需要在指定时间内将异步操作的结果同步返回给前端时;

Controller处理耗时任务,并且需要耗时任务的返回结果时;

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

使用DeferredResult的流程:

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

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

场景

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

如果指定时间内B未给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;
 
    
    private final String requestId = "haha";
 
 
    @GetMapping(value = "/get")
    public DeferredResult<DeferredResultResponse> get(@RequestParam(value = "timeout", required = false, defaultValue = "10000") Long timeout) {
        DeferredResult<DeferredResultResponse> deferredResult = new DeferredResult<>(timeout);
 
        deferredResultService.process(requestId, deferredResult);
 
        return deferredResult;
    }
 
    
    @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;
 
@Service
public class DeferredResultService {
 
    private Map<String, Consumer<DeferredResultResponse>> taskMap;
 
    public DeferredResultService() {
        taskMap = new ConcurrentHashMap<>();
    }
 
    
    public void process(String requestId, DeferredResult<DeferredResultResponse> 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);
    }
 
    
    public void settingResult(String requestId, DeferredResultResponse deferredResultResponse) {
        if (taskMap.containsKey(requestId)) {
            Consumer<DeferredResultResponse> deferredResultResponseConsumer = taskMap.get(requestId);
            // 这里相当于DeferredResult对象的setResult方法
            deferredResultResponseConsumer.accept(deferredResultResponse);
            taskMap.remove(requestId);
        }
    }
 
}

import lombok.Data;
import lombok.Getter;
 
@Data
public 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时抛异常失败的情况,会立刻得到返回结果

总结:

当有前端需要一个耗时操作服务时,可以使用DeferredResult异步机制编写代码。如果不用此功能,要么自己实现类似的功能。要么是前端轮训去拉处理的结果,开发量比较大。有了DeferredResult可以节省很多我们前后端的开发量。

到此这篇关于详解Spring DeferredResult异步操作使用场景的文章就介绍到这了,更多相关Spring DeferredResult异步内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解Spring DeferredResult异步操作使用场景

本文链接: https://lsjlt.com/news/155329.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作