Рубрики
Без рубрики

Spring MVC Async vs Spring Web Flux

Узнайте о Spring Async и Spring Web Flux как теоретически, так и практически с помощью базового нагрузочного теста

Автор оригинала: baeldung.

1. введение

В этом уроке мы рассмотрим аннотацию @Async в Spring MVC, а затем познакомимся с Spring Web Flux. Наша цель-лучше понять разницу между этими двумя понятиями.

2. Сценарий реализации

Здесь мы хотим выбрать сценарий, чтобы показать, как мы можем реализовать простое веб-приложение с помощью каждого из этих API. Кроме того, нам особенно интересно узнать больше об управлении потоками и блокировании или неблокирующем вводе-выводе в каждом конкретном случае.

Давайте выберем веб-приложение с одной конечной точкой, которая возвращает строковый результат. Дело в том, что запрос будет проходить через Фильтр с небольшой задержкой в 200 мс, а затем Контроллеру потребуется 500 мс для вычисления и возврата результата.

Затем мы смоделируем нагрузку с помощью Apache ab на обеих конечных точках и будем отслеживать поведение нашего приложения с помощью JConsole .

Возможно, стоит упомянуть, что в этой статье наша цель-не тест между этими двумя API, а просто небольшой нагрузочный тест, чтобы мы могли отслеживать управление потоками .

3. Асинхронность Spring MVC

Spring 3.0 представила аннотацию @Async . Цель @Async состоит в том, чтобы позволить приложению выполнять задания с большой нагрузкой в отдельном потоке. Кроме того, вызывающий абонент может дождаться результата, если он заинтересован. Следовательно , возвращаемый тип не должен быть void , и он может быть любым из Future , CompletableFuture или ListenableFuture .

Кроме того, Spring 3.2 представил пакет org.springframework.web.context.request.async , который вместе с Servlet 3.0 приносит радость асинхронного процесса на веб-уровень. Таким образом, начиная с весны 3.2, @Async может использоваться в классах, аннотированных как @Controller или @RestController .

Когда клиент инициирует запрос, он проходит через все соответствующие фильтры в цепочке фильтров, пока не достигнет экземпляра DispatcherServlet .

Затем сервлет позаботится об асинхронной отправке запроса. Он помечает запрос как начатый вызовом AsyncWebRequest#startAsync, передает обработку запроса экземпляру WebSyncManager и завершает свою работу без фиксации ответа. Цепочка фильтров также проходит в обратном направлении к корню.

WebAsyncManager отправляет задание обработки запроса в связанный с ним ExecutorService . Всякий раз, когда результат готов, он уведомляет DispatcherServlet о возврате ответа клиенту.

4. Весенняя Асинхронная реализация

Давайте начнем реализацию с написания нашего класса приложения AsyncVsWebFluxApp . H ere, @EnableAsync делает волшебство включения асинхронности для нашего приложения Spring Boot:

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

Тогда у нас есть Асинхронный фильтр , который реализует javax.servlet.Фильтр. Не забудьте смоделировать задержку в методе 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);
    }
}

Наконец, мы разрабатываем наш Асинхронный контроллер с конечной точкой ” /async_result “.:

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

Из-за @Async выше getResultAsync этот метод выполняется в отдельном потоке в приложении по умолчанию ExecutorService . Тем не менее, можно настроить конкретный ExecutorService для нашего метода .

Время испытаний! Давайте запустим приложение, установим Apache ab или любые инструменты для имитации нагрузки. Затем мы можем отправить кучу одновременных запросов через конечную точку “async_result”. Мы можем выполнить консоль и прикрепить ее к нашему java-приложению для мониторинга процесса:

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

5. Поток пружинного полотна

Spring 5.0 представила WebFlux для поддержки реактивной сети неблокирующим способом. Web Flux основан на API реактора, просто еще одна потрясающая реализация реактивного потока .

Поток пружинного полотна поддерживает реактивное противодавление и Сервлет 3.1+ с его неблокирующим вводом-выводом. Следовательно, он может быть запущен на Netty, Undertow, Jetty, Tomcat или любом сервере, совместимом с сервлетом 3.1+.

Хотя все серверы не используют одну и ту же модель управления потоками и управления параллелизмом, Spring WebFlux будет работать нормально, если они поддерживают неблокирующий ввод-вывод и реактивное противодавление.

Spring Web Flux позволяет нам декомпозировать логику декларативным способом с помощью Mono, Flux, и их богатых наборов операторов. Кроме того, у нас могут быть функциональные конечные точки помимо аннотированных @Controller , хотя теперь мы также можем использовать их в Spring MVC .

6. Реализация весеннего веб-потока

Для реализации Web Flux мы идем по тому же пути , что и async. Итак, сначала давайте создадим AsyncVsWebFluxApp :

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

Тогда давайте напишем наш Веб-фильтр потока , который реализует Веб-фильтр. Мы создадим преднамеренную задержку, а затем передадим запрос в цепочку фильтров:

@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)
          );
    }
}

Наконец-то у нас есть наш Веб-контроллер потока . Он предоставляет конечную точку с именем “/flux_result” и возвращает Mono в качестве ответа:

@RestController
public class WebFluxController {

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

Для теста мы используем тот же подход, что и в нашем примере асинхронного приложения. Вот примерный результат для:

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

7. В чем разница?

Spring Async поддерживает спецификации сервлета 3.0, но Spring Web Flux поддерживает сервлет 3.1+. Это приносит ряд отличий:

  • Модель асинхронного ввода-вывода Spring во время ее взаимодействия с клиентом блокируется. Это может вызвать проблемы с производительностью при работе с медленными клиентами. С другой стороны, Spring Web Flux обеспечивает неблокирующую модель ввода-вывода.
  • Чтение тела запроса или частей запроса блокируется в Spring Async, в то время как в Spring WebFlux оно не блокируется.
  • В Spring Async Filter sand Servlet s работают синхронно, но Spring WebFlux поддерживает полную асинхронную связь.
  • Spring Web Flux совместим с более широким диапазоном серверов веб-приложений, чем Spring Async, например Netty и Undertow.

Кроме того, Spring WebFlux поддерживает реактивное противодавление, поэтому у нас больше контроля над тем, как мы должны реагировать на быстрые производители, чем у Spring MVC Async и Spring MVC.

Spring Flux также имеет ощутимый сдвиг в сторону функционального стиля кодирования и декларативной декомпозиции API благодаря API Reactor, стоящему за ним.

Все ли эти элементы приводят нас к использованию Spring Web Flux? Ну, Spring Async или даже Spring MVC могут быть правильным ответом на многие проекты, в зависимости от желаемой масштабируемости нагрузки или доступности системы .

Что касается масштабируемости, Spring Async дает нам лучшие результаты, чем синхронная реализация Spring MVC. Поток пружинного полотна, благодаря своей реактивной природе, обеспечивает нам эластичность и более высокую доступность.

8. Заключение

В этой статье мы узнали больше о Spring Async и Spring Web Flux, а затем провели их теоретическое и практическое сравнение с базовым нагрузочным тестом.

Как всегда, полный код для образца Async и образца Web Flux доступен на GitHub.