1. 概述
在本教程中,我们将研究如何在Spring MVC
使用 DeferredResult 类来执行异步请求处理。
Selvet 3.0
中已经引入了异步的支持,简单来说,它允许在请求接收器线程外的另一个线程中去处理该HTTP请求。
从Spring 3.2
起,就可以使用DeferredResult,它帮助我们将长时间的计算过程从http-worker
线程中分离到一个单独的线程中。
尽管其他线程将占用一些资源用于计算,但工作线程在此期间不会阻塞,仍然可以继续处理传入的客户端请求。
异步请求处理模型非常有用,因为它有助于在高负载时很好地扩展应用程序,特别是对于IO密集型操作。
2. 安装
我们将使用一个 Spring Boot 应用作为例子。然后,我们将展示同步通信和使用DeferredResult 的异步通信,并使用例子比较异步是如何更好地适应高负载和IO密集型。
3. 阻塞的 REST 服务
我们以一个标准的阻塞的 REST 服务开始
1 | @GetMapping("/process-blocking") |
这里的问题是,请求处理线程在处理完成返回结果前是一直被阻塞的,对于需要进行长时间的计算时这不是一个好的解决方案。
4. 使用 DeferredResult 非阻塞的 REST 服务
为了避免阻塞,我们将使用基于回调的模型,我们将返回一个 DeferredResult 到 Servlet 容器中,来取代实际的返回结果。
1 | @GetMapping("/async-deferredresult") |
请求的处理逻辑在一个单独的线程中完成,并且完成之后调用 DeferredResult 类的 setResult 方法来设置实际结果。
下面是日志输出结果,看看是否按照我们的预期顺序输出
1 | Received async-deferredresult request |
在内部,将通知容器线程并将 HTTP 响应返回给给客户端。连接将由容器(servlet 3.0或更高版本)一直保持打开的状态,直到返回相应结果或超时。
5. DeferredResult 回调
我们可以使用DeferredResult 注册3种类型的回调:完成、超时与异常。
我们使用onCompletion
方法来定义一个异步请求完成时执行的代码块
1 | deferredResult.onCompletion(() -> log.info("Processing complete")); |
为了限制请求的处理时间,我们可以在 DeferredResult 实例化时设置一个超时时间,并且可以使用ontTimeout
方法来注册一个自定义的代码块用来在超时后执行。
1 | DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>(500L); |
我们还可以使用onError
方法来注册一个发生异常时的回调。
1 | deferredResult.onError((Throwable t) -> { |