1. 개요

이 예제에서는 Spring의 두 가지 웹 클라이언트 구현인 RestTemplate 과 새로운 Spring 5의 반응형 대체 WebClient 를 비교할 것 입니다.

2. 차단 및 비차단 클라이언트

다른 서비스에 대한 HTTP 호출을 수행하는 것은 웹 애플리케이션의 일반적인 요구 사항입니다. 따라서 웹 클라이언트 도구가 필요합니다.

2.1. RestTemplate 차단 클라이언트

오랫동안 Spring은 RestTemplate 을 웹 클라이언트 추상화로 제공했습니다. 내부적으로 RestTemplate 은 요청당 스레드 모델을 기반으로 하는 Java Servlet API를 사용합니다.

이는 웹 클라이언트가 응답을 받을 때까지 스레드가 차단됨을 의미합니다. 차단 코드의 문제는 각 스레드가 일정량의 메모리와 CPU 주기를 소비하기 때문입니다.

결과를 생성하는 데 필요한 일부 느린 서비스를 기다리는 수신 요청이 많다고 가정해 보겠습니다.

조만간 결과를 기다리는 요청이 쌓일 것입니다. 결과적으로 응용 프로그램은 스레드 풀을 소진하거나 사용 가능한 모든 메모리를 차지하는 많은 스레드를 생성합니다. 또한 빈번한 CPU 컨텍스트(스레드) 전환으로 인해 성능 저하를 경험할 수 있습니다.

2.2. WebClient 비차단 클라이언트

반면에 WebClient 는 Spring Reactive 프레임워크에서 제공하는 비동기식 비차단 솔루션을 사용합니다.

RestTemplate 이 각 이벤트(HTTP 호출)에 대해 호출자 스레드를 사용하는 동안 WebClient 는 각 이벤트에 대한 "작업"과 같은 것을 생성합니다. 이면에서 Reactive 프레임워크는 이러한 "작업"을 Queue에 넣고 적절한 응답을 사용할 수 있을 때만 실행합니다.

Reactive 프레임워크는 이벤트 기반 아키텍처를 사용합니다. Reactive Streams API 를 통해 비동기 로직을 ​​구성하는 수단을 제공합니다 . 결과적으로 반응적 접근 방식은 동기/차단 방식에 비해 더 적은 스레드와 시스템 리소스를 사용하면서 더 많은 논리를 처리할 수 있습니다.

WebClientSpring WebFlux 라이브러리의 일부입니다. 따라서 선언적 구성으로 반응형 유형( MonoFlux )이 있는 기능적이고 유창한 API를 사용하여 클라이언트 코드를 작성할 수도 있습니다 .

3. 비교예

이 두 접근 방식의 차이점을 보여주기 위해 많은 동시 클라이언트 요청으로 성능 테스트를 실행해야 합니다.

특정 수의 병렬 클라이언트 요청 후에 차단 방법으로 상당한 성능 저하를 볼 수 있습니다.

그러나 반응형/비차단 방식은 요청 횟수에 관계없이 일정한 성능을 제공해야 합니다.

이 기사에서는 두 개의 REST Endpoints을 구현합니다. 하나는 RestTemplate 을 사용 하고 다른 하나는 WebClient 를 사용 합니다. 그들의 임무는 트윗 List을 반환하는 또 다른 느린 REST 웹 서비스를 호출하는 것입니다.

시작하려면 Spring Boot WebFlux 스타터 의존성 이 필요합니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

느린 서비스 REST Endpoints은 다음과 같습니다.

@GetMapping("/slow-service-tweets")
private List<Tweet> 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. RestTemplate 을 사용 하여 느린 서비스 호출

이제 웹 클라이언트를 통해 느린 서비스를 호출할 다른 REST Endpoints을 구현해 보겠습니다.

먼저 RestTemplate 을 사용합니다 .

@GetMapping("/tweets-blocking")
public List<Tweet> getTweetsBlocking() {
    log.info("Starting BLOCKING Controller!");
    final String uri = getSlowServiceUri();

    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<List<Tweet>> response = restTemplate.exchange(
      uri, HttpMethod.GET, null,
      new ParameterizedTypeReference<List<Tweet>>(){});

    List<Tweet> result = response.getBody();
    result.forEach(tweet -> log.info(tweet.toString()));
    log.info("Exiting BLOCKING Controller!");
    return result;
}

이 Endpoints을 호출하면 RestTemplate 의 동기 특성으로 인해 코드가 느린 서비스의 응답을 기다리는 것을 차단합니다. 이 메서드의 나머지 코드는 응답을 받은 경우에만 실행됩니다.

다음은 로그에 표시되는 내용입니다.

Starting BLOCKING Controller!
Tweet(text=RestTemplate rules, username=@user1)
Tweet(text=WebClient is better, username=@user2)
Tweet(text=OK, both are useful, username=@user1)
Exiting BLOCKING Controller!

3.2. WebClient 를 사용 하여 느린 서비스 호출

둘째, WebClient 를 사용하여 느린 서비스를 호출해 보겠습니다.

@GetMapping(value = "/tweets-non-blocking", 
            produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Tweet> getTweetsNonBlocking() {
    log.info("Starting NON-BLOCKING Controller!");
    Flux<Tweet> 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;
}

이 경우 WebClient 는 Flux 게시자를 반환 하고 메서드 실행이 완료됩니다. 결과가 나오면 게시자는 구독자에게 트윗을 보내기 시작합니다.

/tweets-non-blocking 엔드포인트를 호출하는 클라이언트(이 경우 웹 브라우저)도 반환된 Flux 객체를 구독하게 됩니다.

이번에는 로그를 관찰해 보겠습니다.

Starting NON-BLOCKING Controller!
Exiting NON-BLOCKING Controller!
Tweet(text=RestTemplate rules, username=@user1)
Tweet(text=WebClient is better, username=@user2)
Tweet(text=OK, both are useful, username=@user1)

이 엔드포인트 메서드는 응답을 받기 전에 완료되었습니다.

4. 결론

이 기사에서는 Spring에서 웹 클라이언트를 사용하는 두 가지 다른 방법을 살펴보았습니다.

RestTemplate 은 Java Servlet API를 사용하므로 동기식이며 차단됩니다.

반대로 WebClient 는 비동기식이며 응답이 돌아올 때까지 기다리는 동안 실행 중인 스레드를 차단하지 않습니다. 알림은 응답이 준비된 경우에만 생성됩니다.

RestTemplate 은 계속 사용됩니다. 그러나 경우에 따라 비차단 접근 방식은 차단 접근 방식에 비해 훨씬 적은 시스템 리소스를 사용합니다. 따라서 WebClient 는 이러한 경우에 선호되는 선택입니다.

기사에서 언급된 모든 코드 스니펫은 GitHub 에서 찾을 수 있습니다 .

REST footer banner