文章目录
- DeferredResult 介绍
- 思路
- Demo搭建
- 1.定义一个抽象的请求体
- 2.定义一个接口返回体
- 3.定义一个接口请求体继承抽象类AsynTaskBaseRequest<T<T>>
- 4.定义seveice类,并声明一个异步方法(Async注解)
- 5.定义一个返回DeferredResult的接口
- 结果验证
DeferredResult 介绍
DeferredResult 是 Spring 框架中的一种异步处理方式,它可以在处理请求时,将请求的结果暂时挂起,等待后续的处理结果再返回给客户端。这种方式可以有效地提高系统的并发处理能力,减少系统的响应时间,提高用户体验。
DeferredResult 的原理是基于 Servlet 3.0规范中的异步处理机制实现的。在 Servlet 3.0中,可以通过AsyncContext 来实现异步处理。当一个请求进入 Servlet 容器时,容器会创建一个 AsyncContext 对象,并将请求的处理交给该对象。在处理请求的过程中,如果需要进行异步处理,可以通过 AsyncContext 对象来实现。
DeferredResult是Spring框架中的一个类,用于实现异步处理和响应。它允许在处理请求时,将结果封装为一个DeferredResult对象,然后将该对象返回给客户端。这样客户端就可以继续执行其他操作,而不需要等待结果返回。 在处理请求的过程中,可以通过调用DeferredResult对象的方法setResult设置结果值。这个过程可以在任何时间点发生,甚至可以在另一个线程中。一旦结果被设置,客户端将收到响应。
tomcat 的线程池大小是有限的,在高并发场景下,如果我们的一些业务逻辑处理慢的话,会渐渐地占满 tomcat 线程,这样就无法处理新的请求,所以一些处理缓慢的业务我们会放到业务线程池中处理,但单纯的放到业务线程池中处理的话,我们无法得知其什么时候处理完,也无法将处理完的结果和之前的请求匹配上,所以常做的方式就是轮询。而 DeferredResult 的做法就类似仅把事情安排好,不会管事情做好没,tomcat 线程就释放走了,注意此时不会给请求方(如浏览器)任何响应,而是将请求存放在一边,咱先不管它,等后面有结果了再把之前的请求拿来,把值响应给请求方。
思路
我们可以借助DeferredResult 的异步处理方式,提前将请求的结果应答给客户端,再执行业务代码,类似于开了一个新的线程。当然这种只适用于客户端与服务端无强制事务关联的场景。
Demo搭建
1.定义一个抽象的请求体
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.async.DeferredResult;public abstract class AsynTaskBaseRequest<T> {protected DeferredResult<ResponseEntity<T>> result;public abstract void makeErrorResponseMsg(int code, String message);public abstract void makeSuccessResponseMsg();protected AsynTaskBaseRequest() {this.result = new DeferredResult<>();}public DeferredResult<ResponseEntity<T>> getResult() {return result;}public void setResult(DeferredResult<ResponseEntity<T>> result) {this.result = result;}
}
2.定义一个接口返回体
import com.fasterxml.jackson.annotation.JsonProperty;public class AsynTaskBaseResponse {@JsonProperty(value = "code")private int code;@JsonProperty(value = "messages")private String messages;public AsynTaskBaseResponse() {}public AsynTaskBaseResponse(int code, String messages) {this.code = code;this.messages = messages;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessages() {return messages;}public void setMessages(String messages) {this.messages = messages;}
}
3.定义一个接口请求体继承抽象类AsynTaskBaseRequest<T>
import com.wjw.service.common.AsynTaskBaseRequest;
import com.wjw.service.common.AsynTaskBaseResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;public class DeferredResultRequestBody extends AsynTaskBaseRequest<AsynTaskBaseResponse> {public DeferredResultRequestBody() {super();}@Overridepublic void makeErrorResponseMsg(int code, String message) {AsynTaskBaseResponse response = new AsynTaskBaseResponse(code, message);ResponseEntity<AsynTaskBaseResponse> responseEntity = new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);result.setResult(responseEntity);}@Overridepublic void makeSuccessResponseMsg() {AsynTaskBaseResponse response = new AsynTaskBaseResponse(0, "success");ResponseEntity<AsynTaskBaseResponse> responseEntity = new ResponseEntity<>(response, HttpStatus.OK);result.setResult(responseEntity);}
}
4.定义seveice类,并声明一个异步方法(Async注解)
使用@Async异步注解时首先要确认你的sprinboot项目是否开启了异步功能,检查启动类中是否加了@EnableAsync注解
Async 在未指定线程池时,使用的是springBoot内置的线程池,那如何指定使用自定义的线程池呢?可参考我的另一篇笔记。
https://blog.csdn.net/weixin_44054222/article/details/107018400?spm=1001.2014.3001.5502
import com.wjw.service.RequestParamer.DeferredResultRequestBody;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service("DeferredResultService")
public class DeferredResultService {@Asyncpublic void demoTest(DeferredResultRequestBody requestBody){try {System.out.println("-----------接口应答前-------------");Thread.sleep(3 * 1000);requestBody.makeSuccessResponseMsg();System.out.println("-----------接口应答后-------------");Thread.sleep(10 * 1000);System.out.println("-----------执行完毕-------------");} catch (Exception e) {requestBody.makeErrorResponseMsg(HttpStatus.BAD_REQUEST.value(), e.getMessage());}}
}
5.定义一个返回DeferredResult的接口
import com.wjw.service.RequestParamer.DeferredResultRequestBody;
import com.wjw.service.common.AsynTaskBaseResponse;
import com.wjw.service.service.DeferredResultService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;@RestController
@RequestMapping("/services/demo/v1")
public class DeferredResultController {@Autowiredprivate DeferredResultService deferredResultService;@GetMapping("/demo/test")public DeferredResult<ResponseEntity<AsynTaskBaseResponse>> test() {DeferredResultRequestBody requestBody = new DeferredResultRequestBody();deferredResultService.demoTest(requestBody);return requestBody.getResult();}
}
结果验证
这时如果调用接口控制台首先会打印"接口应答前",紧接着3s后收到接口的200 ok应答并打印”接口应答后“,等待10s打印”执行完毕“。