You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »


References

CPS-989 - Getting issue details... STATUS
Medium article

Issues & Decisions

#IssueNotes Decision
1


Overview

As specified in the Jira ticket the replacement of RestTemplate became neccessary due the fact that it is not under development anymore and also because it does not support basic function required in the modern dev world such as async calls.


Blocking vs Non-Blocking Client

RestTemplate is a blocking clinet. Which means that an HTTP request will use a thread as long as the request is pending/finishes (thread-per-request model).

This means that a thread will use an extra amount of memory and CPU resource that could lead to performance degradation.

WebClient at the other hand is based on the Spring Reactive framework which provides capability to non-blocking async calls thanks to it's event-driven architecture. Because of this WebClient uses less resources (threads, memory).


Example configuration

Configuration Example
@Slf4j
@Configuration
@AllArgsConstructor
public class WebClientConfiguration {

    public static final int TIMEOUT = 2000;
    private final DmiConfiguration.SdncProperties sdncProperties;

    @Bean
    public WebClient webClient() {
        final var httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT) // millis
                .doOnConnected(connection ->
                        connection
                                .addHandlerLast(new ReadTimeoutHandler(TIMEOUT, TimeUnit.MILLISECONDS)) // millis
                                .addHandlerLast(new WriteTimeoutHandler(TIMEOUT, TimeUnit.MILLISECONDS))); //millis

        return WebClient.builder()
                .baseUrl(sdncProperties.getBaseUrl())
                .defaultHeaders(header -> header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
                .defaultHeaders(header -> header.setBasicAuth(sdncProperties.getAuthUsername(), sdncProperties.getAuthPassword()))
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .filter(logRequest())
                .filter(logResponse())
                .build();
    }

    private ExchangeFilterFunction logRequest() {
        return (clientRequest, next) -> {
            log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
            log.info("--- Http Headers: ---");
            clientRequest.headers().forEach(this::logHeader);
            log.info("--- Http Cookies: ---");
            clientRequest.cookies().forEach(this::logHeader);
            return next.exchange(clientRequest);
        };
    }

    private ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            log.info("Response: {}", clientResponse.statusCode());
            clientResponse.headers().asHttpHeaders()
                    .forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return Mono.just(clientResponse);
        });
    }

    private void logHeader(String name, List values) {
        values.forEach(value -> log.info("{}={}", name, value));
    }
}


Sync call example


Sync Call
String response = webClient
 .get()
 .uri(resourceUrl)
 .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
 .retrieve()
 .onStatus(HttpStatus::is4xxClientError,
 error -> Mono.error(new RuntimeException("API not found")))
 .onStatus(HttpStatus::is5xxServerError,
 error -> Mono.error(new RuntimeException("Server is not responding")))
 .bodyToMono(String.class)
 .block();
return ResponseEntity.ok(response);


Example async call with subscribing


declaration
public Mono<String> httpOperationWithJsonData(final HttpMethod httpMethod, 
												final String resourceUrl, 
												final String jsonData,
												final HttpHeaders httpHeaders) {

return webClient
 .get()
 .uri(resourceUrl)
 .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
 .retrieve()
 .onStatus(HttpStatus::is4xxClientError,
 error -> Mono.error(new RuntimeException("API not found")))
 .onStatus(HttpStatus::is5xxServerError,
 error -> Mono.error(new RuntimeException("Server is not responding")))
 .bodyToMono(String.class);
}


subscribe
httpOperationWithJsonDataAsync(HttpMethod.GET, getResourceUrl, null, httpHeaders)
                .subscribe(response -> {
                    log.info("Get async response : {}", response);
                    responseEntity.getBody().concat(response);
                });
  • No labels