Spring - DeferredResult 异步返回实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring - DeferredResult 异步返回实现相关的知识,希望对你有一定的参考价值。

参考技术A

[TOC]

@Edit by Typora

通常我们经常会遇到一些需要实现异步返回的场景,如长轮询、服务器端处理流程较为复杂并且处理时间比较长的情况,这个时候,如果接受http请求的线程被一直阻塞着,会导致服务器端无法接受处理更多的请求,导致拒绝服务的问题出现,这个时候,将接收请求的线程让出来,会大大提升服务器端并发能力。

Spring在 3.2 的版本上就已经为我们提供的相应的机制,以应对Http Nio的场景。

笔者在以下的示例使用的是 5.2.9.RELEASE 的版本,请读者使用高于 3.2 的版本进行验证。

关于DeferredResult,在Spring的类注释上是这样描述的。

该示例展示了长轮询的场景,方法longPolling模拟长轮询请求,超时时间为30秒。

response方法模拟服务器端处理完结果,并且返回给轮询请求的场景。这种方式只是一种示例,在实际生产中,可能会直接将DeferredResult转交给另外一个线程,进行业务处理,处理完成之后,直接设置返回值,而无需注册到统一的一个缓存中。

长轮询请求示例(对应longPolling方法):

相应请求示例(对应response方法):

上面两个示例是该示例的controller的两个请求,可以使用curl进行尝试,也可以转换为相应的postman请求报文,进行模拟请求。

Spring DeferredResult 异步请求

参考技术A

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

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

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

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

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

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

可以通过 @ExceptionHandler 来处理。

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

配置:

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

以上是关于Spring - DeferredResult 异步返回实现的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Boot 的 DeferredResult REST 方法中使用 Semaphore 服务

带有ajax请求的Spring deferredresult

Spring - DeferredResult 异步返回实现

为啥 DeferredResult 在尝试使用 SSE 时以 setResult() 结束

Spring延迟结果丢失http会话

DeferredResult 实现长轮询