问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

DeferredResult使用详解

创作时间:
作者:
@小白创作中心

DeferredResult使用详解

引用
CSDN
1.
https://m.blog.csdn.net/weixin_44465821/article/details/138468497

DeferredResult是Spring MVC中处理异步请求的重要工具,掌握其使用方法对于提升系统性能和处理能力非常有帮助。本文将详细介绍DeferredResult的基本概念、使用方法和具体应用场景。

DeferredResult简介

Servlet 3.0提供了基于servlet的异步处理API,Spring MVC只是将这些API进行了一系列的封装,从而实现了DeferredResult。DeferredResult字面意思是"延迟结果",它允许Spring MVC收到请求后,立即释放(归还)容器线程,以便容器可以接收更多的外部请求,提升吞吐量。与此同时,DeferredResult将陷入阻塞,直到我们主动将结果set到DeferredResult,最后,DeferredResult会重新申请容器线程,并将本次请求返回给客户端。

DeferredResult的监听器

1. onTimeout()

当deferredResult被创建出来之后,执行setResult()之前,这之间的时间超过设定值时(比如下方案例中设置为5秒超时),则被判定为超时。

DeferredResult<String> deferredResult = new DeferredResult<String>(5 * 1000L);

// 设置超时事件
deferredResult.onTimeout(() -> {
    System.out.println("异步线程执行超时, 异步线程的名称: " + Thread.currentThread().getName());
    deferredResult.setResult("异步线程执行超时");
});

2. onError()

当onTimeout()或onCompletion()等回调函数中的代码报错时,则会执行监听器onError()的回调函数。DeferredResult之外的代码报错不会影响到onError()。

DeferredResult<String> deferredResult = new DeferredResult<String>(5 * 1000L);

// 设置异常事件
deferredResult.onError((throwable) -> {
    System.out.println("异步请求出现错误,异步线程的名称: " + Thread.currentThread().getName() + "异常: " + throwable);
    deferredResult.setErrorResult("异步线程执行出错");
});

3. onCompletion()

代码任意位置调用了同一个 DeferredResult 的setResult()后,则会被 DeferredResult 的onCompletion()监听器捕获到。Spring会任选一条容器线程来执行onCompletion( )中的代码,由于请求线程已被释放(归还),所以此处可能再次由同一条请求线程来处理,也可能由其他线程来处理。

DeferredResult<String> deferredResult = new DeferredResult<String>(5 * 1000L);

// 设置完成事件
deferredResult.onCompletion(() -> {
    System.out.println("异步线程执行完毕,异步线程的名称: " + Thread.currentThread().getName());
});

使用场景

浏览器向A系统发起请求,该请求需要等到B系统(如MQ)给A推送数据时,A才会立刻向浏览器返回数据,如果指定时间内B未给A推送数据,则返回超时。

使用流程

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

代码实现

前端发起查询交易方法(queryOrderPayResult)请求,后端将请求阻塞住 5s,如果在5s之内,支付通知回调(payNotify)过来了,那么之前查询交易的方法立即返回支付结果,否则返回超时了。

@RestController
public class OrderController {
    private static final Logger log = LoggerFactory.getLogger(OrderController.class);
    private static ConcurrentHashMap<String, DeferredResult<String>> DEFERRED_RESULT = new ConcurrentHashMap<>();

    /**
     * 查询订单支付结果
     *
     * @param orderId 订单编号
     * @return DeferredResult
     */
    @GetMapping("queryOrderPayResult")
    public DeferredResult<String> queryOrderPayResult(@RequestParam("orderId") String orderId) {
        log.info("订单orderId:[{}]发起了支付", orderId);
        // 5s 超时
        DeferredResult<String> result = new DeferredResult<>(5000L);
        // 超时操作
        result.onTimeout(() -> {
            DEFERRED_RESULT.get(orderId).setResult("超时了");
        });
        // 完成操作
        result.onCompletion(() -> {
            DEFERRED_RESULT.remove(orderId);
        });
        // 保存此 DeferredResult 的结果
        DEFERRED_RESULT.put(orderId, result);
        return result;
    }

    /**
     * 支付回调
     *
     * @param orderId 订单id
     * @return 支付回调结果
     */
    @GetMapping("payNotify")
    public String payNotify(@RequestParam("orderId") String orderId) {
        if (DEFERRED_RESULT.containsKey(orderId)) {
            Optional.ofNullable(DEFERRED_RESULT.get(orderId)).ifPresent(result -> result.setResult("完成支付"));
            // 设置之前orderId toPay请求的结果
            return "回调处理成功";
        }
        return "回调处理失败";
    }
}

结果演示

超时操作

页面请求 http://localhost:7070/queryOrderPayResult?orderId=12345方法,在5s之内没有setResult 结果,直接返回超时了。

正常操作

页面请求 http://localhost:7070/queryOrderPayResult?orderId=12345方法之后,并立即请求http://localhost:7070/payNotify?orderId=12345方法,得到了正确的结果。

本文原文来自CSDN

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号
DeferredResult使用详解