Реактивное программирование с помощью Spring 5 WebFlux

Введение Spring WebFlux [https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html] - это ответ Spring на растущую проблему блокирующая архитектура ввода-вывода. Поскольку в нашу эпоху данные становятся все более и более важными, меняются подходы, которые мы используем для их извлечения и манипулирования. Условно большинство подходов были «блокирующими», точнее, синхронными. Это означает, что доступ к ресурсу заблокировал доступ приложения к / процессу

Вступление

Spring WebFlux

  • это ответ Spring на растущую проблему блокировки архитектуры ввода-вывода.

Поскольку в нашу эпоху данные становятся все более и более важными, меняются подходы, которые мы используем для их извлечения и манипулирования. Условно большинство подходов были «блокирующими», точнее, синхронными . Это означает, что доступ к одному ресурсу заблокировал доступ приложения / обработку другого ресурса до тех пор, пока не будет обработан предыдущий ресурс.

Это было прекрасно при ограниченном объеме данных и ресурсов, хотя с растущим спросом на данные через высокопроизводительные приложения это стало огромной проблемой.

Издатели начали набирать количество подписчиков и обрабатывать ресурсы один за другим, как если бы один клерк работал во всем супермаркете, и стало слишком медленно для удобного взаимодействия с пользователем.

Решение очевидное - попросите больше клерков обрабатывать клиентов. С точки зрения программных приложений это означает многопоточную среду и асинхронные неблокирующие вызовы.

Пружинный реактивный стек

Популярный стек сервлетов Spring, состоящий из Spring MVC, использует обычные методы доступа и обработки данных в виде синхронных вызовов. С появлением Spring 5 реактивный стек Spring был построен поверх Reactor Core .

Реактивный стек Spring предлагает дополнительную поддержку контейнеров Netty и Servlet 3.1+, повышая производительность для реактивных приложений:

пружинный реакторWebFlux
[Credit Spring]{.small}

Весна WebFlux

Spring WebFlux - это модуль-аналог для Spring MVC. Там, где Spring MVC реализует синхронный, блокирующий ввод-вывод, Spring WebFlux реализует реактивное программирование через реактивные потоки .

Обычно вы используете один или другой, хотя вы также можете комбинировать их вместе.

Зависимости Spring WebFlux

Для простого проекта Spring Boot, инициализированного с помощью Spring Initializr , достаточно добавить одну зависимость:

 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-webflux</artifactId> 
 <version>{version}</version> 
 </dependency> 

spring-boot-starter-webflux включает в себя spring-web , spring-webflux , spring-boot-starter , spring-boot-starter-reactor-netty и т. Д., Поэтому нет необходимости в наличии какой-либо другой зависимости.

Для приложений, использующих Gradle для управления зависимостями, Spring WebFlux можно добавить в файл build.gradle

 compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.2.2.RELEASE' 

Моно и флюс

В Spring WebFlux данные, возвращаемые любой операцией, упаковываются в реактивный поток. Есть два типа, которые воплощают этот подход и являются строительными блоками в приложениях WebFlux - Mono и Flux .

Mono - это поток, который возвращает ноль элементов или один элемент ( 0..1 ), тогда как Flux - это поток, который возвращает ноль или более элементов ( 0..N ).

Mono используется, когда вы ожидаете единственного (или нулевого) результата, такого как получение уникального пользователя из базы данных, тогда как Flux используется, когда вы ожидаете нескольких результатов или какой-либо коллекции.

Контроллер Spring WebFlux

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

Чтобы пометить класс как контроллер, мы используем @RestController на уровне класса.

Наличие зависимостей Spring WebFlux и Reactor Core в пути к классам позволит Spring узнать, что @RestController на самом деле является реактивным компонентом, и добавит поддержку Mono и Flux .

Конфигурация Spring WebFlux

Как и в случае с Spring Boot, мы будем обрабатывать конфигурацию с помощью аннотаций. @Configuration и @EnableWebFlux помечают класс как класс конфигурации, и управление компонентами Spring зарегистрирует его:

 @Configuration 
 @EnableWebFlux 
 public class WebFluxConfig {} 

Чтобы использовать или расширить существующий API конфигурации WebFlux, вы можете расширить интерфейс WebFluxConfigurer

 @Configuration 
 @EnableWebFlux 
 public class WebFluxConfig implements WebFluxConfigurer {} 

CORS с Spring WebFlux

WebFlux также предлагает поддержку CORS (Cross-Origin Resource Sharing) , очень похожую на стек сервлетов Spring MVC. Конфигурация CORS может быть установлена на уровне проекта, а также на уровнях контроллера и метода контроллера.

Чтобы добавить конфигурацию CORS на уровне проекта, вам необходимо @Overrride метод addCorsMappings() из интерфейса WebFluxConfigurer

 @Configuration 
 @EnableWebFlux 
 public class WebFluxConfig implements WebFluxConfigurer { 
 @Override 
 public void addCorsMappings(CorsRegistry registry) { 
 registry.addMapping("/api/**") 
 .allowedOrigins("http://www.stackabuse.com") 
 .allowedMethods("GET", "PUT", "DELETE") 
 .allowedHeaders("testHeader") 
 .allowCredentials(true); 
 } 
 } 

А для добавления конфигурации CORS на более детальном уровне @CrossOrigin аннотация @CrossOrigin. Это позволяет указывать детали CORS на уровне контроллера и метода.

Когда @CrossOrigin используется на уровне класса контроллера, все настройки CORS уровня метода наследуются от конфигурации уровня класса:

 @CrossOrigin(origins = "https://www.stackabuse.com") 
 @RestController 
 @RequestMapping("/resource") 
 public class ResourceController { 
 
 @GetMapping("/{id}") 
 public Mono<Resource> getResource(@PathVariable String id) { 
 
 } 
 } 

Безопасность с помощью Spring Webflux

Spring также предлагает стандартные параметры безопасности для своего фреймворка WebFlux. Вам нужно добавить @EnableWebFluxSecurity на уровне класса, чтобы включить конфигурации безопасности в проекте:

 @EnableWebFluxSecurity 
 public class WebfluxSecurity { 
 
 @Bean 
 public SecurityWebFilterChain springSecurityFilterChain( 
 ServerHttpSecurity http) { 
 http.csrf().disable() 
 .authorizeExchange() 
 .pathMatchers(HttpMethod.GET, "/resource/").hasRole("ADMIN") 
 .pathMatchers("/**").permitAll() 
 .and() 
 .httpBasic(); 
 return http.build(); 
 } 
 } 

Веб-клиент Spring WebFlux

Spring WebFlux также включает реактивный веб-клиент для управления вызовами REST. Reactor-Netty используется по умолчанию для связи с сервером WebFlux.

Клиентский объект WebFlux может быть создан либо с использованием статических фабричных методов, либо с помощью его метода построителя (дополнительная настройка).

Статические фабричные методы - это WebClient.create() и WebClient.create(String baseUrl) . С другой стороны, WebClient.builder предлагает следующие параметры для добавления дополнительных сведений к объекту веб-клиента:

  • uriBuilderFactory
  • defaultHeader
  • defaultCookie
  • defaultRequest
  • filter
  • exchangeStrategies
  • clientConnector

Мы более подробно рассмотрим это в следующем разделе, где создается демонстрационное приложение.

Демо-приложение

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

Веб-сервер WebFlux

Репозиторий

Начнем с того, что заложим основу для реактивного приложения, создав интерфейс реактивного репозитория.

 public interface ResourceRepository extends ReactiveCrudRepository<Resource, String> {} 

Мы расширили наш репозиторий с помощью ReactiveCrudRepository WebFlux, который будет возвращать данные как Mono или Flux , в зависимости от количества элементов, которые могут быть извлечены.

Чтобы использовать MongoDB с Spring WebFlux, мы добавим класс конфигурации, который сообщает Spring, что база данных должна обрабатываться как реактивный компонент:

 @EnableReactiveMongoRepositories 
 public class MongoDbConfiguration extends AbstractReactiveMongoConfiguration { 
 
 @Override 
 public MongoClient reactiveMongoClient() { 
 return MongoClients.create(); 
 } 
 
 @Override 
 protected String getDatabaseName() { 
 return "testDatabase"; 
 } 
 } 

Контроллер

Когда наш уровень данных готов и настроен, давайте создадим простой контроллер REST, который будет извлекать ресурсы Mono и Flux через запросы GET:

 @RestController 
 @RequestMapping("/resource") 
 public class ResourceController { 
 
 @Autowired 
 ResourceRepository resourceRepository; 
 
 @GetMapping("/{id}") 
 public Mono<Resource> getResource(@PathVariable String id) { 
 return resourceRepository.findById(id); 
 } 
 
 @GetMapping 
 public Flux<Resource> getResources() { 
 return resourceRepository.findAll(); 
 } 
 } 

Здесь мы используем наш реактивный ResourceRepository чтобы найти ресурс по id полученному из запроса. Поскольку мы ищем уникальный идентификатор, ожидаемым результатом будет либо 1 запись (ресурс с переданным идентификатором найден), либо 0 записей (в базе данных нет записей), поэтому в качестве возвращаемого типа используется Mono

Поскольку findAll() может возвращать более одного элемента ресурса (если он присутствует), Flux используется в качестве возвращаемого типа.

Веб-клиент WebFlux

Теперь, когда у нас настроено базовое приложение REST, давайте создадим клиент Spring WebFlux, который может отправлять запросы в приложение REST WebFlux.

Чтобы запустить веб-клиент, нам нужно создать WebClient используя URL-адрес сервера:

 public WebClient openConnection(String url) { 
 client = WebClient.create(url); 
 return client; 
 } 

После создания WebClient мы можем использовать его для запросов к веб-серверу.

Сделаем запрос к серверу, получив ресурс с заданным ID:

 public void getResourceById(String id) { 
 Mono<Resource> result = client.get() 
 .uri("/resource/{id}", "1") 
 .retrieve() 
 .bodyToMono(Resource.class); 
 
 result.subscribe(System.out::println); 
 } 

Метод bodyToMono() отвечает за упаковку тела ответа в Mono .

Точно так же, если вызывающая конечная точка возвращает данные как Flux мы можем получить их, используя следующий метод:

 public void getAllResources() { 
 Flux<Resource> result = client.get() 
 .uri("/resource") 
 .retrieve() 
 .bodyToFlux(Resource.class); 
 
 result.subscribe(System.out::println); 
 } 

Заключение

Spring Framework позволяет разработчикам создавать реактивные, неблокирующие приложения и API, используя стек Spring WebFlux. WebFlux предлагает аннотации, очень похожие на те, что используются в классических приложениях Spring MVC, что упрощает разработчикам переход на реактивный код.

В этом руководстве мы рассмотрели наиболее важные концепции фреймворка WebFlux и создали демонстрационное приложение, чтобы продемонстрировать их на практике.

Исходный код можно найти на GitHub .

comments powered by Disqus