Автор оригинала: Drazen Nikolic.
1. введение
В этом уроке мы сравним две реализации веб – клиента Spring – RestTemplate | и реактивную альтернативу new Spring 5 WebClient .
2. Блокировка против Неблокирующий клиент
Это обычное требование в веб-приложениях для выполнения HTTP-вызовов к другим службам. Поэтому нам нужен инструмент веб-клиента.
2.1. Клиент блокировки RestTemplate
В течение длительного времени Spring предлагал RestTemplate в качестве абстракции веб-клиента. Под капотом RestTemplate использует API сервлетов Java, который основан на модели потока на запрос .
Это означает, что поток будет блокироваться до тех пор, пока веб-клиент не получит ответ. Проблема с блокирующим кодом связана с тем, что каждый поток потребляет некоторое количество памяти и циклов процессора.
Давайте рассмотрим множество входящих запросов, которые ждут некоторого медленного обслуживания, необходимого для получения результата.
Рано или поздно запросы, ожидающие результатов, будут накапливаться. Следовательно, приложение создаст много потоков, которые исчерпают пул потоков или займут всю доступную память . Мы также можем испытывать снижение производительности из-за частого переключения контекста процессора (потока).
2.2. Неблокирующий Клиент WebClient
С другой стороны, Веб-клиент использует асинхронное, неблокирующее решение, предоставляемое Spring Reactive framework .
В то время как RestTemplate использует поток вызывающего абонента для каждого события (HTTP-вызов), WebClient создаст что-то вроде “задачи” для каждого события. За кулисами реактивный фреймворк будет ставить эти “задачи” в очередь и выполнять их только тогда, когда будет доступен соответствующий ответ.
Реактивный фреймворк использует архитектуру, управляемую событиями. Он предоставляет средства для создания асинхронной логики через API реактивных потоков . В результате реактивный подход может обрабатывать больше логики при использовании меньшего количества потоков и системных ресурсов по сравнению с синхронным/блокирующим методом.
Веб-клиент является частью библиотеки Spring Web Flux . Поэтому мы можем дополнительно написать клиентский код, используя функциональный, свободный API с реактивными типами ( Mono и Flux ) в качестве декларативной композиции .
3. Пример Сравнения
Чтобы продемонстрировать различия между этими двумя подходами, нам нужно будет выполнить тесты производительности с большим количеством одновременных клиентских запросов. Мы увидим значительное снижение производительности при использовании метода блокировки после определенного количества параллельных клиентских запросов.
С другой стороны, реактивный/неблокирующий метод должен обеспечивать постоянную производительность, независимо от количества запросов.
Для целей этой статьи давайте реализуем две конечные точки REST, одна из которых использует RestTemplate , а другая – WebClient . Их задача-вызвать другой медленный веб-сервис REST, который возвращает список твитов.
Для начала нам понадобится зависимость Spring Boot Web Flux starter :
org.springframework.boot spring-boot-starter-webflux
Кроме того, вот наша конечная точка отдыха медленного обслуживания:
@GetMapping("/slow-service-tweets") private ListgetAllTweets() { Thread.sleep(2000L); // delay return Arrays.asList( new Tweet("RestTemplate rules", "@user1"), new Tweet("WebClient is better", "@user2"), new Tweet("OK, both are useful", "@user1")); }
3.1. Использование RestTemplate для вызова медленной службы
Давайте теперь реализуем другую конечную точку REST, которая будет вызывать нашу медленную службу через веб-клиент.
Во-первых, мы будем использовать RestTemplate :
@GetMapping("/tweets-blocking") public ListgetTweetsBlocking() { log.info("Starting BLOCKING Controller!"); final String uri = getSlowServiceUri(); RestTemplate restTemplate = new RestTemplate(); ResponseEntity > response = restTemplate.exchange( uri, HttpMethod.GET, null, new ParameterizedTypeReference
>(){}); List
result = response.getBody(); result.forEach(tweet -> log.info(tweet.toString())); log.info("Exiting BLOCKING Controller!"); return result; }
Когда мы вызываем эту конечную точку из-за синхронного характера RestTemplate , код блокирует ожидание ответа от нашей медленной службы. Только после получения ответа будет выполнен остальной код в этом методе. В журналах мы увидим:
Starting BLOCKING Controller! Tweet(text=RestTemplate rules, [email protected]) Tweet(text=WebClient is better, [email protected]) Tweet(text=OK, both are useful, [email protected]) Exiting BLOCKING Controller!
3.2. Использование WebClient для вызова медленной службы
Во-вторых, давайте использовать WebClient для вызова медленной службы:
@GetMapping(value = "/tweets-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public FluxgetTweetsNonBlocking() { log.info("Starting NON-BLOCKING Controller!"); Flux tweetFlux = WebClient.create() .get() .uri(getSlowServiceUri()) .retrieve() .bodyToFlux(Tweet.class); tweetFlux.subscribe(tweet -> log.info(tweet.toString())); log.info("Exiting NON-BLOCKING Controller!"); return tweetFlux; }
В этом случае Веб-клиент возвращает Flux publisher и выполнение метода будет завершено. Как только результат будет доступен, издатель начнет рассылать твиты своим подписчикам. Обратите внимание, что клиент (в данном случае веб-браузер), вызывающий эту /твиты-неблокирующую конечную точку, также будет подписан на возвращаемый объект Flux .
Давайте на этот раз понаблюдаем за журналом:
Starting NON-BLOCKING Controller! Exiting NON-BLOCKING Controller! Tweet(text=RestTemplate rules, [email protected]) Tweet(text=WebClient is better, [email protected]) Tweet(text=OK, both are useful, [email protected])
Обратите внимание, что этот метод конечной точки был завершен до получения ответа.
4. Заключение
В этой статье мы рассмотрели два различных способа использования веб-клиентов весной.
RestTemplate использует Java Servlet API и поэтому является синхронным и блокирующим. Напротив, Веб-клиент является асинхронным и не блокирует выполняющийся поток в ожидании ответа. Уведомление будет получено только тогда, когда ответ будет готов.
RestTemplate по-прежнему будет использоваться. В некоторых случаях неблокирующий подход использует гораздо меньше системных ресурсов по сравнению с блокирующим. Следовательно, в этих случаях Веб-клиент является предпочтительным выбором.
Все фрагменты кода, упомянутые в статье, можно найти на GitHub .