Spring WebClient vs. RestTemplate

1. 簡介

本教程中,咱們將對比 Spring 的兩種 Web 客戶端實現 —— RestTemplate 和 Spring 5 中全新的 Reactive 替代方案 WebClientjava

2. 阻塞式 vs 非阻塞式客戶端

Web 應用中,對其餘服務進行 HTTP 調用是一個很常見的需求。所以,咱們須要一個 Web 客戶端工具。react

2.1. RestTemplate 阻塞式客戶端

很長一段時間以來,Spring 一直提供 RestTemplate 做爲 Web 客戶端抽象。在底層,RestTemplate 使用了基於每一個請求對應一個線程模型(thread-per-request)的 Java Servlet API。git

這意味着,直到 Web 客戶端收到響應以前,線程都將一直被阻塞下去。而阻塞代碼帶來的問題則是,每一個線程都消耗了必定的內存和 CPU 週期。github

讓咱們考慮下有不少傳入請求,它們正在等待產生結果所需的一些慢服務。web

等待結果的請求早晚都會堆積起來。**所以,程序將建立不少線程,這些線程將耗盡線程池或佔用全部可用內存。**因爲頻繁的 CPU 上下文(線程)切換,咱們還會遇到性能降低的問題。spring

2.2. WebClient 非阻塞式客戶端

另外一方面,WebClient 使用 Spring Reactive Framework 所提供的異步非阻塞解決方案。瀏覽器

RestTemplate 爲每一個事件(HTTP 請求)建立一個新的 線程 時,WebClient 將爲每一個事件建立相似於「任務」的東東。幕後,Reactive 框架將對這些 「任務」 進行排隊,並僅在適當的響應可用時執行它們。多線程

Reactive 框架使用事件驅動的體系結構。它提供了經過 Reactive Streams API 組合異步邏輯的方法。所以,與同步/阻塞方法相比,Reactive 可使用更少的線程和系統資源來處理更多的邏輯。併發

WebClientSpring WebFlux 庫的一部分。所以,咱們還可使用流暢的函數式 API 編寫客戶端代碼,並將響應類型(Mono 和 Flux)做爲聲明來進行組合。app

3. 案例對比

爲了演示兩種方法間的差別,咱們須要使用許多併發客戶端請求來運行性能測試。在必定數量的併發請求後,咱們將看到阻塞方法性能的顯著降低。

另外一方面,不管請求數量如何,反應式/非阻塞方法均可以提供恆定的性能。

就本文而言,**讓咱們實現兩個 REST 端點,一個使用 RestTemplate,另外一個使用 WebClient。**他們的任務是調用另外一個響應慢的 REST Web 服務,該服務返回一個 Tweet List。

首先,咱們須要引入 Spring Boot WebFlux starter 依賴

<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. 使用 RestTemplate 調用慢服務

如今,讓咱們來實現另外一個 REST 端點,它將經過 Web 客戶端調用咱們的慢服務。

首先,咱們來使用 RestTemplate

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

    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<list<tweet>&gt; response = restTemplate.exchange(
      uri, HttpMethod.GET, null,
      new ParameterizedTypeReference<list<tweet>&gt;(){});

    List<tweet> result = response.getBody();
    result.forEach(tweet -&gt; 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. 使用 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 -&gt; log.info(tweet.toString()));
    log.info("Exiting NON-BLOCKING Controller!");
    return tweetFlux;
}

本例中,WebClient 返回一個 Flux 生產者後完成方法的執行。一旦結果可用,發佈者將開始向其訂閱者發送 tweets。注意,調用 /tweets-non-blocking 這個端點的客戶端(本例中的 Web 瀏覽器)也將訂閱返回的 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 中使用 Web 客戶端的兩種不一樣方式。

RestTemplate 使用 Java Servlet API,所以是同步和阻塞的。相反,WebClient 是異步的,在等待響應返回時不會阻塞正在執行的線程。只有當程序就緒時,纔會產生通知。

RestTemplate 仍將會被使用。但在某些狀況下,與阻塞方法相比,非阻塞方法使用的系統資源要少得多。所以,在這些狀況下,WebClient 不失爲是更好的選擇。

文中提到的全部代碼片斷,都可在 GitHub 上找到。

> 原文:<https: www.baeldung.com spring-webclient-resttemplate> > > 做者:Drazen Nikolic > > 譯者:萬想


送福利啦~ 近期將以前已翻譯文章,整理成了PDF。 ​ 在公衆號後臺回覆:002便可領取哦~ ​ 後續也會不斷更新PDF的內容,敬請期待!

img</https:></tweet></tweet></tweet></list<tweet></list<tweet></tweet></tweet>

相關文章
相關標籤/搜索