0%

Spring DeferredResult 指南(译)

1. 概述

在本教程中,我们将研究如何在Spring MVC使用 DeferredResult 类来执行异步请求处理。

Selvet 3.0中已经引入了异步的支持,简单来说,它允许在请求接收器线程外的另一个线程中去处理该HTTP请求。

Spring 3.2起,就可以使用DeferredResult,它帮助我们将长时间的计算过程从http-worker线程中分离到一个单独的线程中。

尽管其他线程将占用一些资源用于计算,但工作线程在此期间不会阻塞,仍然可以继续处理传入的客户端请求。

异步请求处理模型非常有用,因为它有助于在高负载时很好地扩展应用程序,特别是对于IO密集型操作。

2. 安装

我们将使用一个 Spring Boot 应用作为例子。然后,我们将展示同步通信和使用DeferredResult 的异步通信,并使用例子比较异步是如何更好地适应高负载和IO密集型。

3. 阻塞的 REST 服务

我们以一个标准的阻塞的 REST 服务开始

1
2
3
4
5
@GetMapping("/process-blocking")
public ResponseEntity<?> handleReqSync() {
// ...
return ResponseEntity.ok("ok");
}

这里的问题是,请求处理线程在处理完成返回结果前是一直被阻塞的,对于需要进行长时间的计算时这不是一个好的解决方案。

4. 使用 DeferredResult 非阻塞的 REST 服务

为了避免阻塞,我们将使用基于回调的模型,我们将返回一个 DeferredResult 到 Servlet 容器中,来取代实际的返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@GetMapping("/async-deferredresult")
public DeferredResult<ResponseEntity<?>> handleReqDefResult() {
log.info("Received async-deferredresult request");
DeferredResult<ResponseEntity<?>> output = new DeferredResult<>();

ForkJoinPool.commonPool().submit(() -> {
log.info("Processing in separate thread");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
}
output.setResult(ResponseEntity.ok("ok"));
});

log.info("servlet thread freed");
return output;
}

请求的处理逻辑在一个单独的线程中完成,并且完成之后调用 DeferredResult 类的 setResult 方法来设置实际结果。

下面是日志输出结果,看看是否按照我们的预期顺序输出

1
2
3
Received async-deferredresult request
servlet thread freed
Processing in separate thread

在内部,将通知容器线程并将 HTTP 响应返回给给客户端。连接将由容器(servlet 3.0或更高版本)一直保持打开的状态,直到返回相应结果或超时。

5. DeferredResult 回调

我们可以使用DeferredResult 注册3种类型的回调:完成、超时与异常。

我们使用onCompletion 方法来定义一个异步请求完成时执行的代码块

1
deferredResult.onCompletion(() -> log.info("Processing complete"));

为了限制请求的处理时间,我们可以在 DeferredResult 实例化时设置一个超时时间,并且可以使用ontTimeout 方法来注册一个自定义的代码块用来在超时后执行。

1
2
3
4
5
6
DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>(500L);

deferredResult.onTimeout(() ->
deferredResult.setErrorResult(
ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Request timeout occurred.")));

我们还可以使用onError 方法来注册一个发生异常时的回调。

1
2
3
4
5
deferredResult.onError((Throwable t) -> {
deferredResult.setErrorResult(
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An error occurred."));
});

原文地址

https://www.baeldung.com/spring-deferred-result