post-image

So sánh WebClient và RestTemplate trong Spring Boot

Tổng quan

Trong bài viết trước mình đã giới thiệu tới mọi người 2 khái niệm đó là WebClientRestTemplate. Bài viết này chúng ta hãy cùng đi so sánh WebClient và RestTemplate.

Blocking và Non-Blocking Client

Đây là một yêu cầu phổ biến trong các ứng dụng web để thực hiện các cuộc gọi HTTP đến các dịch vụ khác. Do đó, chúng tôi cần một công cụ web client.

RestTemplate Blocking Client

Trong một khoảng thời gian dài, Spring cung cấp cho chúng ta class RestTemplate như một ứng dụng web client trừu tượng. Về cơ bản, RestTemplate sử dụng Java Servlet API, dựa trên mô hình thread cho mỗi  request.

Điều này có nghĩa là thread sẽ chặn cho đến khi web client nhận được phản hồi. Vấn đề với mã chặn đó là do mỗi thread đều tiêu thụ bộ nhớ và CPU.

Hãy tưởng tượng có nhiều request được gửi đến, và đang chờ một số dịch vụ cần thiết để tạo ra kết quả.

Không sớm thì muộn, những request đang chờ đợi kết quả sẽ ngày một nhiều hơn. Do đó, ứng dụng sẽ tạo ra nhiều thread, điều này làm cạn kiệt thread pool hoặc chiếm tất cả bộ nhớ có sẵn. Chúng ta cũng có thể gặp phải tình trạng giảm hiệu suất do chuyển đổi ngữ cảnh CPU thường xuyên.

WebClient Non-Blocking Client

Mặt khác, WebClient sử dụng giải pháp không đồng bộ, giải pháp non-blocking được cung cấp bởi framework Spring Reactive.

Trong khi RestTemplate sử dụng các thread cho mỗi sự kiện, WebClient sẽ tạo ra một task cho mỗi sự kiện. Spring Reactive sẽ sắp xếp các “task” đó và chỉ thực thi chúng khi có response thích hợp.

Reactive framework sử dụng kiến ​​trúc hướng sự kiện. Nó cung cấp các phương tiện để xử lý logic không đồng bộ thông qua API Reactive Streams. 

WebClient là một phần của thư viện Spring WebFlux. Do đó, chúng ta có thể viết thêm client code bằng cách sử dụng một API thông thạo, có chức năng với các kiểu phản ứng (Mono và Flux) làm thành phần khai báo.

Ví dụ so sánh WebClient và RestTemplate

Để chứng minh sự khác biệt giữa hai cách tiếp cận này, chúng ta cần chạy dự án để kiểm tra hiệu suất với nhiều client request một lúc.

Chúng ta sẽ thấy hiệu suất sẽ bị giảm đi đáng kể với phương thức Blocking sau một số request từ client.

Mặt khác, phương thức non-blocking sẽ cho hiệu suất tốt hơn, bất kể số lượng request là bao nhiêu.

Đầu tiên chúng ta thêm thư viện Spring Boot WebFlux starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>Code language: HTML, XML (xml)

slow Service REST endpoint:

@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"));
}Code language: PHP (php)

Sử dụng RestTemplate để gọi Slow Service

  • Đầu tiên chúng ta sử dụng 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;
}Code language: PHP (php)
  • Khi chúng ta gọi endpoint này, do tính đồng bộ của RestTemplate, mã sẽ block các response từ slow service của chúng ta. Chỉ khi response đã được nhận, phần còn lại của mã trong phương thức này sẽ được thực thi.
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!

Sử dụng WebClient để gọi Slow Service

  • Tiếp theo chúng ta sẽ sử dụng WebClient để gọi slow service
@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;
}Code language: JavaScript (javascript)
  • Trong trường hợp này, WebClient trả về một Flux và việc thực thi phương thức được hoàn tất. Sau khi có kết quả trả về, publisher sẽ bắt đầu gửi các tweet cho người đăng ký của mình. Lưu ý rằng một web client (trong trường hợp này là trình duyệt web) gọi endpoint này  tweets-non-block cũng sẽ được đăng ký đối tượng Flux trả về.

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)

Kết luận

Trong bài viết này, chúng ta đã tìm hiểu hai cách khác nhau để sử dụng ứng dụng web client trong Spring.

RestTemplate sử dụng Java Servlet API và do đó đồng bộ và block. Ngược lại, WebClient là không đồng bộ và non-blocking thread đang thực thi trong khi chờ response trở lại. Chỉ khi response đã sẵn sàng thì thông báo mới được tạo ra.

RestTemplate sẽ vẫn được sử dụng. Trong một số trường hợp, phương pháp non-blocking sử dụng ít tài nguyên hệ thống hơn nhiều so với phương pháp blocking. Do đó WebClient là một lựa chọn tốt hơn.

Leave a Reply

Your email address will not be published. Required fields are marked *