文章目录
- 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();
}
@Override
public void makeErrorResponseMsg(int code, String message) {
AsynTaskBaseResponse response = new AsynTaskBaseResponse(code, message);
ResponseEntity<AsynTaskBaseResponse> responseEntity = new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
result.setResult(responseEntity);
}
@Override
public void makeSuccessResponseMsg() {
AsynTaskBaseResponse response = new AsynTaskBaseResponse(0, "success");
ResponseEntity<AsynTaskBaseResponse> responseEntity = new ResponseEntity<>(response, HttpStatus.OK);
result.setResult(responseEntity);
}
}
4.定义seveice类,并声明一个异步方法(Async注解)
使用@Async异步注解时首先要确认你的springboot项目是否开启了异步功能,检查启动类中是否加了@EnableAsync注解
Async 在未指定线程池时,使用的是springBoot内置的线程池,那如何指定使用自定义的线程池呢?可参考我的另一篇笔记。
https://blog.csdn.net/weixin_44054222/article/details/107018400
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 {
@Async
public 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 {
@Autowired
private DeferredResultService deferredResultService;
@GetMapping("/demo/test")
public DeferredResult<ResponseEntity<AsynTaskBaseResponse>> test() {
DeferredResultRequestBody requestBody = new DeferredResultRequestBody();
deferredResultService.demoTest(requestBody);
return requestBody.getResult();
}
}
结果验证
这时如果调用接口控制台首先会打印"接口应答前",紧接着3s后收到接口的200 ok应答并打印”接口应答后“,等待10s打印”执行完毕“。