使用DeferredResult来设计异步接口

文章目录

    • DeferredResult 介绍
    • 思路
    • Demo搭建
      • 1.定义一个抽象的请求体
      • 2.定义一个接口返回体
      • 3.定义一个接口请求体继承抽象类AsynTaskBaseRequest>
      • 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

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注解

使用DeferredResult来设计异步接口_第1张图片
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打印”执行完毕“。

使用DeferredResult来设计异步接口_第2张图片

你可能感兴趣的:(DeferredResult,接口异步)