Spring MVC Async vs Spring WebFlux – Spring MVC Async vs Spring WebFlux

最后修改: 2020年 8月 23日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll explore the @Async annotation in Spring MVC, and then we’ll get familiar with Spring WebFlux. Our goal is to have a better understanding of the difference between these two.

在本教程中,我们将探讨Spring MVC中的@Async注解,然后我们将熟悉Spring WebFlux。我们的目标是更好地理解这两者之间的区别。

2. Implementation Scenario

2.实施方案

Here, we want to choose a scenario to show how we can implement a simple web application with each of these APIs. Moreover, we’re especially interested to see more about thread management and blocking or non-blocking I/O in each case.

在这里,我们想选择一个场景来展示我们如何用这些API中的每一个来实现一个简单的网络应用。此外,我们特别想看到更多关于线程管理和每个案例中的阻塞或非阻塞I/O。

Let’s choose a web application with one endpoint that returns a string result. The point here is that the request will pass through a Filter with a small 200ms delay, and then the Controller needs 500ms to calculate and return the result.

让我们选择一个有一个返回字符串结果的端点的Web应用。这里的重点是,请求将通过一个Filter,有200ms的小延迟,然后Controller需要500ms来计算并返回结果。

Next, we’re going to simulate a load with Apache ab on both endpoints and monitor our app behavior with JConsole.

接下来,我们将在两个端点上用Apache ab模拟一个负载,并用JConsole监控我们的应用程序行为。

It may worth mentioning that in this article, our goal is not a benchmark between these two APIs, just a small load test so we can trace the thread management.

值得一提的是,在这篇文章中,我们的目标不是这两个API之间的基准,只是一个小的负载测试,以便我们可以跟踪线程管理

3. Spring MVC Async

3.SpringMVC的异步性

Spring 3.0 introduced the @Async annotation. @Async‘s goal is to allow the application to run heavy-load jobs on a separate thread. Also, the caller can wait for the result if interested. Hence the return type must not be void, and it be can be any of Future, CompletableFuture, or ListenableFuture.

Spring 3.0引入了@Async注解。@Async的目标是允许应用程序在一个单独的线程上运行重载作业。另外,如果有兴趣的话,调用者可以等待结果。因此,返回类型不能是void,它可以是FutureCompletableFutureListenableFuture中的任何一个。

Moreover, Spring 3.2 introduced the org.springframework.web.context.request.async package that, together with Servlet 3.0, brings the joy of the asynchronous process to the web layer. Thus, since Spring 3.2, @Async can be used in classes annotated as @Controller or @RestController.

此外,Spring 3.2引入了org.springframework.web.context.request.async包,该包与Servlet 3.0一起将异步过程的乐趣带到了Web层。因此,从Spring 3.2开始,@Async可以被用于注释为@Controller@RestController的类中。

When the client initiates a request, it goes through all matched filters in the filter chain until it arrives at the DispatcherServlet instance.

当客户端发起请求时,它经过过滤器链中所有匹配的过滤器,直到它到达DispatcherServlet实例。

Then, the servlet takes care of the async dispatching of the request. It marks the request as started by calling AsyncWebRequest#startAsync, transfers the request handling to an instance of WebSyncManager, and finishes its job without committing the response. The filter chain also is traversed in the reverse direction to the root.

然后,servlet负责处理请求的异步分派。它通过调用AsyncWebRequest#startAsync将请求标记为开始,将请求处理转移到WebSyncManager的一个实例,并在不提交响应的情况下完成其工作。过滤器链也以相反的方向遍历到根部。

WebAsyncManager submits the request processing job in its associated ExecutorService. Whenever the result is ready, it notifies DispatcherServlet for returning the response to the client.

WebAsyncManager在其关联的ExecutorService中提交请求处理工作。每当结果准备好了,它就通知DispatcherServlet向客户端返回响应。

4. Spring Async Implementation

4.Spring的异步实现

Let’s start the implementation by writing our application class, AsyncVsWebFluxApp. Here, @EnableAsync does the magic of enabling async for our Spring Boot application:

让我们从编写我们的应用类AsyncVsWebFluxApp开始实现。在这里,@EnableAsync为我们的Spring Boot应用程序实现了启用异步的魔法。

@SpringBootApplication
@EnableAsync
public class AsyncVsWebFluxApp {
    public static void main(String[] args) {
        SpringApplication.run(AsyncVsWebFluxApp.class, args);
    }
}

Then we have AsyncFilter, which implements javax.servlet.Filter. Don’t forget to simulate the delay in the doFilter method:

然后我们有AsyncFilter,它实现了javax.servlet.Filter。别忘了在doFilter方法中模拟延迟。

@Component
public class AsyncFilter implements Filter {
    ...
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
        // sleep for 200ms 
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

Finally, we develop our AsyncController with the “/async_result” endpoint:

最后,我们用”/async_result“端点开发我们的AsyncController

@RestController
public class AsyncController {
    @GetMapping("/async_result")
    @Async
    public CompletableFuture getResultAsyc(HttpServletRequest request) {
        // sleep for 500 ms
        return CompletableFuture.completedFuture("Result is ready!");
    }
}

Because of the @Async above getResultAsync, this method is executed in a separate thread on the application’s default ExecutorService. However, it is possible to set up a specific ExecutorService for our method.

由于@Async上面的getResultAsync,这个方法是在应用程序的默认ExecutorService上的独立线程中执行的。然而,我们可以为我们的方法设置一个特定的ExecutorService

Test time! Let’s run the application, install Apache ab, or any tools to simulate the load. Then we can send a bunch of concurrent requests over the “async_result” endpoint. We can execute JConsole and attach it to our java application to monitor the process:

测试时间!让我们运行应用程序,安装Apache ab,或任何工具来模拟负载。然后我们可以通过 “async_result “端点发送一堆并发的请求。我们可以执行JConsole,并将其附加到我们的java应用中,以监控这个过程。

ab -n 1600 -c 40 localhost:8080/async_result

 

5. Spring WebFlux

5.Spring的WebFlux

Spring 5.0 has introduced WebFlux to support the reactive web in a non-blocking manner. WebFlux is based on the reactor API, just another awesome implementation of the reactive stream.

Spring 5.0 引入了WebFlux,以非阻塞的方式支持反应式网络。WebFlux基于reactor API,只是反应式流的另一个出色的实现。

Spring WebFlux supports reactive backpressure and Servlet 3.1+ with its non-blocking I/O. Hence, it can be run on Netty, Undertow, Jetty, Tomcat, or any Servlet 3.1+ compatible server.

Spring WebFlux支持反应式背压和Servlet 3.1+,其非阻塞式I/O。因此,它可以在Netty、Undertow、Jetty、Tomcat或任何Servlet 3.1+兼容服务器上运行。

Although all servers don’t use the same thread management and concurrency control model, Spring WebFlux will work fine as long as they are supporting non-blocking I/O and reactive backpressure.

虽然所有的服务器都不使用相同的线程管理和并发控制模型,但只要它们支持非阻塞I/O和反应式反压,Spring WebFlux就能正常工作。

Spring WebFlux allows us to decompose the logic in a declarative way with Mono, Flux, and their rich operator sets. Moreover, we can have functional endpoints besides its @Controller annotated ones, though we can now also use these in Spring MVC.

Spring WebFlux允许我们通过Mono、Flux及其丰富的操作符集以声明的方式分解逻辑。此外,除了其@Controller注解的端点之外,我们还可以拥有功能端点,尽管我们现在也可以在Spring MVC中使用这些端点。

6. Spring WebFlux Implementation

6.Spring WebFlux的实现

For WebFlux implementation, we go the same path as async. So at first, let’s create the AsyncVsWebFluxApp:

对于WebFlux的实现,我们走的是和ync一样的路。因此,首先,让我们创建AsyncVsWebFluxApp

@SpringBootApplication
public class AsyncVsWebFluxApp {
    public static void main(String[] args) {
        SpringApplication.run(AsyncVsWebFluxApp.class, args);
    }
}

Then let’s write our WebFluxFilter, which implements WebFilter. We’ll generate an intentional delay and then pass the request to the filter chain:

然后让我们编写我们的WebFluxFilter,它实现了WebFilter.我们将产生一个故意的延迟,然后将请求传递给过滤器链。

@Component
public class WebFluxFilter implements org.springframework.web.server.WebFilter {

    @Override
    public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        return Mono
          .delay(Duration.ofMillis(200))
          .then(
            webFilterChain.filter(serverWebExchange)
          );
    }
}

At last, we have our WebFluxController. It exposes an endpoint called “/flux_result” and returns a Mono<String> as the response:

最后,我们有了我们的WebFluxController。它暴露了一个名为“/flux_result”/em>的端点,并返回一个Mono<String>作为响应。

@RestController
public class WebFluxController {

    @GetMapping("/flux_result")
    public Mono getResult(ServerHttpRequest request) {
       return Mono.defer(() -> Mono.just("Result is ready!"))
         .delaySubscription(Duration.ofMillis(500));
    }
}

For the test, we’re taking the same approach as with our async sample application. Here’s the sample result for:

在测试中,我们采取的方法与我们的异步示例应用程序相同。下面是示例的结果:

ab -n 1600 -c 40 localhost:8080/flux_result

7. What’s the Difference?

7.有什么区别?

Spring Async supports Servlet 3.0 specifications, but Spring WebFlux supports Servlet 3.1+. It brings a number of differences:

Spring Async支持Servlet 3.0规范,但Spring WebFlux支持Servlet 3.1+。它带来了一些差异。

  • Spring Async I/O model during its communication with the client is blocking. It may cause a performance problem with slow clients. On the other hand, Spring WebFlux provides a non-blocking I/O model.
  • Reading the request body or request parts is blocking in Spring Async, whiles it is non-blocking in Spring WebFlux.
  • In Spring Async, Filters and Servlets are working synchronously, but Spring WebFlux supports full asynchronous communication.
  • Spring WebFlux is compatible with wider ranges of Web/Application servers than Spring Async, like Netty, and Undertow.

Moreover, Spring WebFlux supports reactive backpressure, so we have more control over how we should react to fast producers than both Spring MVC Async and Spring MVC.

此外,Spring WebFlux支持反应式背压,因此与Spring MVC Async和Spring MVC相比,我们对应该如何对快速生产者做出反应有更多的控制。

Spring Flux also has a tangible shift towards functional coding style and declarative API decomposition thanks to Reactor API behind it.

由于Reactor API的支持,Spring Flux也切实地转向了函数式编码风格和声明式API分解。

Do all of these items lead us to use Spring WebFlux? Well, Spring Async or even Spring MVC might be the right answer to a lot of projects out there, depending on the desired load scalability or availability of the system.

所有这些项目都会让我们使用Spring WebFlux吗?那么,Spring Async甚至Spring MVC可能是外面很多项目的正确答案,这取决于所需的负载扩展性或系统的可用性

Regarding scalability, Spring Async gives us better results than synchronous Spring MVC implementation. Spring WebFlux, because of its reactive nature, provides us elasticity and higher availability.

关于可扩展性,Spring Async比Spring MVC的同步实现给了我们更好的结果。Spring WebFlux,由于其反应性,为我们提供了弹性和更高的可用性。

8. Conclusion

8.结语

In this article, we learned more about Spring Async and Spring WebFlux, then we had a comparison of them theoretically and practically with a basic load test.

在这篇文章中,我们了解了更多关于Spring Async和Spring WebFlux的信息,然后我们用一个基本的负载测试对它们进行了理论和实践上的比较。

As always, complete code for the Async sample and the WebFlux sample are available over GitHub.

一如既往,Async 示例WebFlux 示例的完整代码可通过 GitHub 获得。