Spring WebClient vs. RestTemplate
1. Introdução
Neste tutorial, vamos comparar duas das implementações de cliente da web do Spring -RestTemplatee nova alternativa reativa do Spring 5WebClient.
2. Bloqueio vs. Cliente sem bloqueio
É um requisito comum em aplicativos da web fazer chamadas HTTP para outros serviços. Portanto, precisamos de uma ferramenta de cliente da web.
2.1. RestTemplate Cliente de bloqueio
Há muito tempo, o Spring ofereceRestTemplate como uma abstração de cliente web. Sob o capô,RestTemplate uses the Java Servlet API, which is based on the thread-per-request model.
Isso significa que o encadeamento será bloqueado até que o cliente da web receba a resposta. O problema com o código de bloqueio é devido a cada thread consumir uma certa quantidade de memória e ciclos de CPU.
Vamos considerar ter muitas solicitações de entrada, que estão esperando por algum serviço lento necessário para produzir o resultado.
Mais cedo ou mais tarde, as solicitações que aguardam os resultados se acumularão. Consequently, the application will create many threads, which will exhaust the thread pool or occupy all the available memory. Também podemos sofrer degradação no desempenho devido à alternância frequente de contexto da CPU (thread).
2.2. WebClient Cliente não bloqueador
Por outro lado,WebClient uses an asynchronous, non-blocking solution provided by the Spring Reactive framework.
EnquantoRestTemplate usa o thread do chamador para cada evento (chamada HTTP),WebClient criará algo como uma “tarefa” para cada evento. Nos bastidores, a estrutura Reativa enfileirará essas “tarefas” e as executará somente quando a resposta apropriada estiver disponível.
A estrutura Reativa usa uma arquitetura orientada a eventos. Ele fornece meios para compor a lógica assíncrona por meio deReactive Streams API. Como resultado, a abordagem reativa pode processar mais lógica enquanto utiliza menos threads e recursos do sistema, em comparação com o método síncrono / bloqueio.
WebClient faz parte da bibliotecaSpring WebFlux. Portanto,we can additionally write client code using a functional, fluent API with reactive types (Mono and Flux) as a declarative composition.
3. Exemplo de Comparação
Para demonstrar as diferenças entre essas duas abordagens, precisaríamos executar testes de desempenho com muitas solicitações de cliente simultâneas. Veríamos uma degradação significativa de desempenho com o método de bloqueio após um certo número de solicitações de clientes paralelos.
Por outro lado, o método reativo / sem bloqueio deve fornecer desempenhos constantes, independentemente do número de solicitações.
Para os fins deste artigo,let’s implement two REST endpoints, one using RestTemplate and the other using WebClient. Sua tarefa é chamar outro serviço da web REST lento, que retorna uma lista de tweets.
Para começar, precisaremos deSpring Boot WebFlux starter dependency:
org.springframework.boot
spring-boot-starter-webflux
Além disso, aqui está nosso endpoint REST de serviço lento:
@GetMapping("/slow-service-tweets")
private List getAllTweets() {
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. UsandoRestTemplate para chamar um serviço lento
Vamos agora implementar outro endpoint REST que chamará nosso serviço lento por meio do cliente da web.
Em primeiro lugar, usaremosRestTemplate:
@GetMapping("/tweets-blocking")
public List getTweetsBlocking() {
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;
}
Quando chamamos este endpoint, devido à natureza síncrona deRestTemplate, o código irá bloquear a espera pela resposta de nosso serviço lento. Somente quando a resposta for recebida, o restante do código neste método será executado. Nos registros, veremos:
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. UsandoWebClient para chamar um serviço lento
Em segundo lugar, vamos usarWebClient para chamar o serviço lento:
@GetMapping(value = "/tweets-non-blocking",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux getTweetsNonBlocking() {
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;
}
Nesse caso,WebClient retorna um publicadorFlux e a execução do método é concluída. Quando o resultado estiver disponível, o editor começará a emitir tweets para seus assinantes. Observe que um cliente (neste caso, um navegador da web) chamando este ponto de extremidade/tweets-non-blocking também será inscrito no objetoFlux retornado.
Vamos observar o log desta vez:
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])
Observe que este método do nó de extremidade foi concluído antes da resposta ser recebida.
4. Conclusão
Neste artigo, exploramos duas maneiras diferentes de usar clientes da Web no Spring.
RestTemplate usa Java Servlet API e, portanto, é síncrono e bloqueador. Ao contrário,WebClient é assíncrono e não bloqueará o thread em execução enquanto espera pela resposta de volta. Only when the response is ready will the notification be produced.
RestTemplate ainda será usado. Em alguns casos, a abordagem sem bloqueio usa muito menos recursos do sistema em comparação com a abordagem de bloqueio. Portanto, nesses casos,WebClient é uma escolha preferível.
Todos os trechos de código, mencionados no artigo, podem ser encontradosover on GitHub.