您好,登錄后才能下訂單哦!
本教程中,我們將對比 Spring 的兩種 Web 客戶端實現 —— RestTemplate 和 Spring 5 中全新的 Reactive 替代方案 WebClient。
Web 應用中,對其他服務進行 HTTP 調用是一個很常見的需求。因此,我們需要一個 Web 客戶端工具。
很長一段時間以來,Spring 一直提供 RestTemplate 作為 Web 客戶端抽象。在底層,RestTemplate 使用了基于每個請求對應一個線程模型(thread-per-request)的 Java Servlet API。
這意味著,直到 Web 客戶端收到響應之前,線程都將一直被阻塞下去。而阻塞代碼帶來的問題則是,每個線程都消耗了一定的內存和 CPU 周期。
讓我們考慮下有很多傳入請求,它們正在等待產生結果所需的一些慢服務。
等待結果的請求遲早都會堆積起來。因此,程序將創建很多線程,這些線程將耗盡線程池或占用所有可用內存。由于頻繁的 CPU 上下文(線程)切換,我們還會遇到性能下降的問題。
另一方面,WebClient 使用 Spring Reactive Framework 所提供的異步非阻塞解決方案。
當 RestTemplate 為每個事件(HTTP 請求)創建一個新的 線程 時,WebClient 將為每個事件創建類似于“任務”的東東。幕后,Reactive 框架將對這些 “任務” 進行排隊,并僅在適當的響應可用時執行它們。
Reactive 框架使用事件驅動的體系結構。它提供了通過 Reactive Streams API 組合異步邏輯的方法。因此,與同步/阻塞方法相比,Reactive 可以使用更少的線程和系統資源來處理更多的邏輯。
WebClient 是 Spring WebFlux 庫的一部分。因此,我們還可以使用流暢的函數式 API 編寫客戶端代碼,并將響應類型(Mono 和 Flux)作為聲明來進行組合。
為了演示兩種方法間的差異,我們需要使用許多并發客戶端請求來運行性能測試。在一定數量的并發請求后,我們將看到阻塞方法性能的顯著下降。
另一方面,無論請求數量如何,反應式/非阻塞方法都可以提供恒定的性能。
就本文而言,讓我們實現兩個 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"));
}
現在,讓我們來實現另一個 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>> 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!
其次,讓我們使用 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。注意,調用 /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)
注意,此端點的方法在收到響應之前就已完成。
本文中,我們探討了在 Spring 中使用 Web 客戶端的兩種不同方式。
RestTemplate 使用 Java Servlet API,因此是同步和阻塞的。相反,WebClient 是異步的,在等待響應返回時不會阻塞正在執行的線程。只有當程序就緒時,才會產生通知。
RestTemplate 仍將會被使用。但在某些情況下,與阻塞方法相比,非阻塞方法使用的系統資源要少得多。因此,在這些情況下,WebClient 不失為是更好的選擇。
文中提到的所有代碼片段,均可在 GitHub 上找到。
原文:<https://www.baeldung.com/spring-webclient-resttemplate>
作者:Drazen Nikolic
譯者:萬想
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。