Spring DeferredResult 异步请求

网友投稿 272 2022-09-26

Spring DeferredResult 异步请求

Spring DeferredResult 异步请求

​​一、背景​​​​二、分析​​​​三、实现要求​​​​四、后端代码实现​​​​五、运行结果​​

​​1、超时操作​​​​2、正常操作​​

​​六、DeferredResult运行原理​​​​六、注意事项​​

​​1、异常的处理​​​​2、异步过程中的拦截器。​​

​​七、完整代码​​​​八、参考链接​​

一、背景

最近在做项目的过程中,有一个支付的场景,前端需要根据支付的结果,跳转到不同的页面中。而我们的支付通知是支付方异步通知回来的,因此在发出支付请求后 无法立即获取到支付结果,此时我们就需要轮训交易结果,判断是否支付成功。

二、分析

要实现后端将支付结果通知给前端,实现的方式有很多种。

ajax 轮训长轮训websocketsse…

经过考虑,最终决定使用 ​​长轮训​​​ 来实现。 而 Spring 的 ​​DeferredResult​​​ 是一个异步请求,正好可以用来实现长轮训。而这个异步是基于 ​​Servlet3​​的异步来实现的,在Spring中DeferredResult结果会另起线程来处理,并不会占用容器(Tomcat)的线程,因此还能提高程序的吞吐量。

三、实现要求

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

四、后端代码实现

package com.huan.study.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.context.request.async.DeferredResult;import javax.annotation.PostConstruct;import java.util.Optional;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;/** * 订单控制器 * * @author huan.fu 2021/10/14 - 上午9:34 */@RestControllerpublic class OrderController { private static final Logger log = LoggerFactory.getLogger(OrderController.class); private static volatile ConcurrentHashMap> DEFERRED_RESULT = new ConcurrentHashMap<>(20000); private static volatile AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0); @PostConstruct public void printRequestCount() { Executors.newSingleThreadScheduledExecutor() .scheduleAtFixedRate(() -> { log.error("" + ATOMIC_INTEGER.get()); }, 10, 1, TimeUnit.SECONDS); } /** * 查询订单支付结果 * * @param orderId 订单编号 * @return DeferredResult */ @GetMapping("queryOrderPayResult") public DeferredResult queryOrderPayResult(@RequestParam("orderId") String orderId) { log.info("订单orderId:[{}]发起了支付", orderId); ATOMIC_INTEGER.incrementAndGet(); // 3s 超时 DeferredResult result = new DeferredResult<>(3000L); // 超时操作 result.onTimeout(() -> { DEFERRED_RESULT.get(orderId).setResult("超时了"); log.info("订单orderId:[{}]发起支付,获取结果超时了.", orderId); }); // 完成操作 result.onCompletion(() -> { log.info("订单orderId:[{}]完成.", orderId); 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) { log.info("订单orderId:[{}]支付完成回调", orderId); // 默认结果发生了异常 if ("123".equals(orderId)) { DEFERRED_RESULT.get(orderId).setErrorResult(new RuntimeException("订单发生了异常")); return "回调处理失败"; } if (DEFERRED_RESULT.containsKey(orderId)) { Optional.ofNullable(DEFERRED_RESULT.get(orderId)).ifPresent(result -> result.setResult("完成支付")); // 设置之前orderId toPay请求的结果 return "回调处理成功"; } return "回调处理失败"; }}

五、运行结果

1、超时操作

2、正常操作

六、DeferredResult运行原理

Controller 返回一个 DeferredResult 对象,并且把它保存在一个可以访问的内存队列或列表中。Spring Mvc 开始异步处理。同时,DispatcherServlet 和所有配置的过滤器退出请求处理线程,但Response(响应)保持打开状态。应用程序从某个线程设置 DeferredResult,Spring MVC 将请求分派回 Servlet 容器。DispatcherServlet 再次被调用,并以异步产生的返回值恢复处理 。

六、注意事项

1、异常的处理

可以通过 ​​@ExceptionHandler​​ 来处理。

2、异步过程中的拦截器。

可以通过 ​​DeferredResultProcessingInterceptor​​​ 或者 ​​AsyncHandlerInterceptor​​​ 来实现。需要注意看拦截器方法上的注释,有些方法,如果调用了​​setResult​​等是不会再次执行的。

配置:

/** * 如果加了 @EnableWebMvc 注解的话, Spring 很多默认的配置就没有了,需要自己进行配置 * * @author huan.fu 2021/10/14 - 上午10:39 */@Configurationpublic class WebConfig implements WebMvcConfigurer { @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { // 默认超时时间 60s configurer.setDefaultTimeout(60000); // 注册 deferred result 拦截器 configurer.registerDeferredResultInterceptors(new CustomDeferredResultProcessingInterceptor()); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CustomAsyncHandlerInterceptor()).addPathPatterns("/**"); }}

七、完整代码

​​https://gitee.com/huan1993/spring-cloud-parent/tree/master/springboot/spring-deferred-result​​

八、参考链接

​​https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async-deferredresult​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Baby决定改变:不想孩子被人说,你妈妈做什么都不行!
下一篇:SpringBoot整合多个RabbitMQ
相关文章

 发表评论

暂时没有评论,来抢沙发吧~