Вступление
- это ответ Spring на растущую проблему блокировки архитектуры ввода-вывода.
Поскольку в нашу эпоху данные становятся все более и более важными, меняются подходы, которые мы используем для их извлечения и манипулирования. Условно большинство подходов были «блокирующими», точнее, синхронными . Это означает, что доступ к одному ресурсу заблокировал доступ приложения / обработку другого ресурса до тех пор, пока не будет обработан предыдущий ресурс.
Это было прекрасно при ограниченном объеме данных и ресурсов, хотя с растущим спросом на данные через высокопроизводительные приложения это стало огромной проблемой.
Издатели начали набирать количество подписчиков и обрабатывать ресурсы один за другим, как если бы один клерк работал во всем супермаркете, и стало слишком медленно для удобного взаимодействия с пользователем.
Решение очевидное - попросите больше клерков обрабатывать клиентов. С точки зрения программных приложений это означает многопоточную среду и асинхронные неблокирующие вызовы.
Пружинный реактивный стек
Популярный стек сервлетов Spring, состоящий из Spring MVC, использует обычные методы доступа и обработки данных в виде синхронных вызовов. С появлением Spring 5 реактивный стек Spring был построен поверх Reactor Core .
Реактивный стек Spring предлагает дополнительную поддержку контейнеров Netty и Servlet 3.1+, повышая производительность для реактивных приложений:
[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 .