Spring WebClient vs. RestTemplate

HTTP Client-Side,REST,Spring
Remote
1
08:44 AM · Dec 01 ,2025

1. 概述

在本教程中,我們將比較 Spring 的兩個 Web 客户端實現——RestTemplate 和 Spring 5 引入的反應式替代方案 WebClient

2. 客户端阻塞與非阻塞

在Web應用程序中,向其他服務發出HTTP調用是很常見的需求。因此,我們需要一個Web客户端工具。

2.1. RestTemplate 阻塞客户端

長期以來,Spring一直提供 RestTemplate 作為Web客户端抽象。在底層, RestTemplate 使用Java Servlet API,該API基於線程一請求模型。

這意味着線程將阻塞,直到Web客户端收到響應。阻塞代碼的問題在於,每個線程消耗一定數量的內存和CPU週期。

讓我們考慮有大量傳入請求,這些請求正在等待某個慢速服務產生結果。

總而言之,等待結果的請求會堆積起來。 因此,應用程序會創建許多線程,這些線程會耗盡線程池或佔用所有可用內存。 我們還可能因為頻繁的CPU上下文(線程)切換而導致性能下降。

2.2. WebClient 非阻塞客户端

另一方面, WebClient 使用Spring Reactive框架提供的異步、非阻塞解決方案。

RestTemplate 為每個事件使用調用者線程時,WebClient 將為每個事件創建一個類似“任務”。在後台,Reactive框架會排隊這些“任務”並在適當的響應可用時才執行它們。

Reactive框架使用事件驅動架構。它提供了通過Reactive Streams API組合異步邏輯的手段。因此,與同步/阻塞方法相比,反應式方法可以在使用更少的線程和系統資源的同時處理更多邏輯。

WebClient 是Spring WebFlux庫的一部分。 因此,我們也可以使用反應式類型(MonoFlux)的函數式、流暢API編寫客户端代碼,以聲明式的方式組合。

3. Comparison Example

為了演示這兩種方法之間的差異,我們需要運行性能測試,模擬大量併發客户端請求。

使用阻塞方法後,在一定數量的並行客户端請求數達到一定程度時,性能會顯著下降。

然而,反應式/非阻塞方法應該始終保持穩定的性能,無論請求數量如何。

在本文中,我們將實現兩個 REST 端點,一個使用 RestTemplate,另一個使用 WebClient。它們的任務是調用另一個慢速 REST Web 服務,該服務返回一列表的推文。

首先,我們需要 Spring Boot WebFlux 啓動器依賴項:Spring Boot WebFlux starter dependency:


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

以下是我們的慢速服務 REST 端點:


@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. Using RestTemplate to Call a Slow Service

現在,我們將實現另一個 REST 端點,該端點將通過 WebClient 調用我們的慢速服務。

首先,我們將使用 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;
}

當調用此端點時,由於 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. Using WebClient to Call a Slow Service

第二,我們將使用 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 發佈器,並且方法執行完成。一旦結果可用,發佈器將開始向其訂閲者發出推文。

客户端(在本例中為 Web 瀏覽器)調用此 /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. 結論

在本文中,我們探討了兩種使用 Web 客户端在 Spring 中的方法。

RestTemplate 使用 Java Servlet API,因此是同步且阻塞的。

相反,WebClient 是異步的,並且在等待響應返回時不會阻塞執行線程。 只有響應準備好時才會產生通知。

RestTemplate 仍然將被使用。 但是在某些情況下,非阻塞方法與阻塞方法相比,使用的系統資源要少得多。 因此,WebClient 在這些情況下是首選的方案。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.