Дисклеймер
В этой статье речь пойдет главным образом о WebFlux, работающем на базе Netty в рамках Spring, а не о чистом использовании Project Reactor.
Функции высшего порядка — это функции, которые могут принимать в качестве аргументов другие функции или возвращать их как результат. Такой подход позволяет писать более гибкий и читаемый код, особенно при работе с обработкой данных или асинхронными операциями. В Java и Kotlin элементы функционального программирования уже присутствуют, однако строгий функциональный подход редко используется на практике. Тем не менее, даже небольшое использование таких возможностей делает код более выразительным и компактным.
Таким образом, асинхронность позволяет эффективнее использовать потоки, переключая задачи между ними. Как это реализовано? Это сделано через прерываемость: в хорошем асинхронном коде любой метод может быть прерван в момент ожидания, а поток переключится на другую задачу, пока не получит ответ от первой.
Если традиционное приложение может обслуживать ограниченное количество одновременных пользователей (в зависимости от количества потоков), WebFlux способен обрабатывать тысячи соединений, эффективно управляя потоками и избегая блокировок.
@GetMapping("/test")
fun test(): Mono<String> {
return Mono.just("Hello, WebFlux")
.map { value -> value.uppercase() }
}
Mono — это контейнер, который мы наполняем данными и можем изменять их с помощью таких операторов, как map. Важно, что выполнение кода не происходит сразу, а только при подписке на этот Mono.
fun getResults(): Flux<String> {
val urls = listOf("/endpoint1", "/endpoint2", "/endpoint3")
return Flux.fromIterable(urls)
.flatMap { url ->
webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String::class.java)
}
}
WebClient отправляет запросы, не блокируя основную нить выполнения, а поток просто переключается на другие задачи, пока ожидает ответа от сервера.
val restTemplate = RestTemplate()
fun getResults(): Flux<String> {
val urls = listOf("/endpoint1", "/endpoint2", "/endpoint3")
return Flux.fromIterable(urls)
.map { url ->
client.getForObject(
"http://localhost:8084$url",
String::class.java
)
}
}
val restTemplate = RestTemplate()
fun getResults(): Flux<String> {
val urls = listOf("/endpoint1", "/endpoint2", "/endpoint3")
return Flux.fromIterable(urls)
.flatMap { url ->
Mono.fromCallable {
client.getForObject(
"http://localhost:8084$url",
String::class.java
)
}.publishOn(Schedulers.boundedElastic())
}
}
public Flux<Data> createDataStream() {
final HeavyObject heavyObject = new HeavyObject();
return Flux.create(sink -> {
// Лямбда сохраняет ссылку на heavyObject
sink.next(heavyObject);
sink.complete();
});
}