Для начала неплохо бы понять, что такое реактивное программирование или Reactive Programming. Я прочёл кучу статей и пришёл к выводу:
Reactive Programming — это такая хипстерская и хайповая парадигма программирования, которая предполагает, что обработка данных происходит неблокирующими потоками.
Как и для Agile у нас есть манифест для реактивного программирования: https://www.reactivemanifesto.org.
Попробуем создать наш реактивный RESTful WEB Service на Spring 5. Переходим на Spring Initializr. Выбираем Maven Project, Java и Spring Boot 2. Затем в Dependencies прописываем зависимость Reactive Web.
Создаём обработчик HTTP-запроса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/** * <p> * <a href="https://urvanov.ru>https://urvanov.ru</a> * </p> * <p> * <b>LICENSE:</b> * </p> * <p> * Do what you want. * </p> */ package ru.urvanov.javaexamples.reactivewebexample; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; /** * @author Fedor Urvanov * */ @Component public class ConcatenateHandler { public Mono<ServerResponse> hello(ServerRequest request) { return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN) .body(BodyInserters .fromObject(request.queryParam("str1").orElse("") + request.queryParam("str2").orElse(""))); } } |
Мы просто соединяем строки, переданные нам в параметрах str1 и str2. Тут всё просто и понятно.
Теперь нам нужно написать роутер, который будет связывать URL с нашим обработчиком:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/** * <p> * <a href="https://urvanov.ru>https://urvanov.ru</a> * </p> * <p> * <b>LICENSE:</b> * </p> * <p> * Do what you want. * </p> */ package ru.urvanov.javaexamples.reactivewebexample; /** * @author Fedor Urvanov * */ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.server.RequestPredicates; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; @Configuration public class ReactiveWebRouterConfig { @Bean public RouterFunction<ServerResponse> route(ConcatenateHandler concatenateHandler) { // We listen /concatenate and return our Concatenate Handler return RouterFunctions .route(RequestPredicates.GET("/concatenate") .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), concatenateHandler::hello); } } |
Тут тоже всё довольно просто. Мы просто обрабатываем GET-запросы на «/concatenate» и возвращаем наш ConcatenateHandler.
Теперь напишем клиента, который будет вызывать наш сервис.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/** * <p> * <a href="https://urvanov.ru>https://urvanov.ru</a> * </p> * <p> * <b>LICENSE:</b> * </p> * <p> * Do what you want. * </p> */ package ru.urvanov.javaexamples.reactivewebexample; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; /** * @author Fedor Urvanov * */ public class ConcatenateWebClient { public String getResult(String str1, String str2) { Mono<ClientResponse> result = WebClient.create().get().uri(builder -> builder.scheme("http") .host("localhost").port(8080).path("concatenate") .queryParam("str1", str1) .queryParam("str2", str2) .build()).accept(MediaType.TEXT_PLAIN).exchange(); return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block(); } public static void main(String[] args) { ConcatenateWebClient webClient = new ConcatenateWebClient(); System.out.println(webClient.getResult("Vasya", "Petya")); } } |
Теперь запускаем наше Spring Boot приложение ReactiveWebExampleApplication.
Потом запускаем ConcatenateWebClient и в консоли видим результат:
1 |
>> result = VasyaPetya |
Итог: Мы написали пример простого приложения на Spring WebFlux. Сейчас Spring WebFlux довольно активно продвигается.
Постулаты реактивного программирования, вроде как, вполне здравые:
- отзывчивость;
- отказоустойчивость;
- гибкость;
- основанность на модели асинхронной обработки событий.
Но пока я не увидел какого-либо действительно кардинального улучшения, из-за которого бы действительно стоило бросаться переписывать свои старые сервисы со Spring MVC на Spring WebFlux. Да и вообще, есть вероятность, что это очередная волна хайпа (очень похоже на волны хайпа с Groovy, Scala и Kotlin), которая вполне может стухнуть спустя какое-то время.
Если я что-то не понял, то можете изъяснить свою точку зрения в комментариях.
Исходный код проекта на GitHub.
Пока всё. Да прибудет с вами Сила.
мне кажется пример будет более понятен если
getResult вернет не String через блокировку общего потока, а непосредственно Mono на результат которого можно подписаться, ничего не блокируя.
и тогда:
public static void main(String[] args) {
ConcatenateWebClient webClient = new ConcatenateWebClient();
webClient.getResult(«Vasya», «Petya»).subscribe(
// что-то делаем при получении результата
System.out.println(«Результат получен!»);
);
System.out.println(«Ждем результат…»);
}