筆者在之前已經寫了一系列的關于RestTemplate的文章,如下:
- 精講RestTemplate第1篇-在Spring或非Spring環境下如何使用
- 精講RestTemplate第2篇-多種底層HTTP客戶端類別庫的切換
- 精講RestTemplate第3篇-GET請求使用方法詳解
- 精講RestTemplate第4篇-POST請求方法使用詳解
- 精講RestTemplate第5篇-DELETE、PUT等請求方法使用詳解
- 精講RestTemplate第6篇-檔案上傳下載與大檔案流式下載
- 精講RestTemplate第7篇-自定義請求失敗例外處理
- 精講RestTemplate第8篇-請求失敗自動重試機制
- 精講RestTemplate第9篇-如何通過HTTP Basic Auth認證
- 精講RestTemplate第10篇-使用代理作為跳板發送請求
RestTemplate作為spring-web專案的一部分,在Spring 3.0版本開始被引入,根據Spring官方檔案及原始碼中的介紹,RestTemplate在將來的版本中它可能會被棄用, 作為替代,Spring官方已在Spring 5中引入了WebClient作為非阻塞式Reactive HTTP客戶端,

一、什么是回應式非阻塞IO
在開始為大家介紹webClient之前有必要為大家介紹一下回應式非阻塞IO與傳統IO之前的區別,我們先留下一個問題:webClient發送與接收單個HTTP請求比RestTemplate更快么?答案是否定的, 看到這里有的同學已經蒙了,既然webClient沒有更快,那官方為什么還推薦使用它?聽我往下講,
1.1.傳統阻塞式IO模型
筆者用相對通俗的話為大家說明一下阻塞IO與非阻塞IO之間的區別,我們以軟體開發團隊的作業方式來做一個比喻,作為軟體開發人員,我們肯定知道軟體開發的基本流程:
- 專案立項與可行性研究
- 需求分析與設計
- 代碼開發
- 迭代測驗
- 上線及配置管理、運維

在以Spring MVC或者struct為代表的框架都是基于sevlet的,其底層IO模型是阻塞IO模型,這種模型就好像你是公司的一個開發人員,上面的所有的5項作業全都由你一個人完成,如果公司有10個人,最多就只能同時進行10個需求,客戶需求增多了也沒有辦法,只能讓他們等著,如下圖:一個請求占用一個執行緒,當執行緒池內的執行緒都被占用后新來的請求就只能等待,
1.2.回應式IO模型
spring 社區為了解決Spring MVC的阻塞模型在高并發場景下的性能瓶頸的問題,推出了Spring WebFlux,WebFlux底層實作是久經考驗的netty非阻塞IO通信框架,該框架的請求處理與執行緒互動關系圖如下:

boosGroup用于Accetpt連接建立事件并分發請求, workerGroup用于處理I/O讀寫事件,netty我就不細說了,還是用通俗的方式給大家講一下:如果通俗的將上圖中的各個任務池、執行緒池的組合比做一個軟體開發公司,那么:
- 專案立項及可研,由公司專案經理及顧問來完成
- 需求分析與設計,由產品經理和架構師來完成
- 代碼研發,由專案經理帶領開發人員來完成
- 迭代測驗,由測驗團隊來完成
- 上線及配置管理、運維,可能由專門的devops團隊來完成
這樣一個公司內的所有人完成分工,就能在有限的資源的情況下,去接觸更多的客戶,談更多的需求,合理的分配人力資源,達到并發處理能力最大化的極限水平,相比于一個員工從頭到位的負責一個專案,它的組織性更強,分工更明確,合理的利用空閑資源,專業的人最專業的事,
這種人力資源的合理利用及組織方式和非阻塞IO模型有異曲同工之處,通過合理的將請求處理執行緒及任務進行分類,合理的利用系統的記憶體、CPU資源,達到單位時間內處理能力的最大化就是異步非阻塞IO的核心用意! 回到上文給大家留下的問題,webClient處理單個HTTP請求的回應時長并不比RestTemplate更快,但是它處理并發的能力更強,所以回應式非阻塞IO模型的核心意義在于:提高了單位時間內有限資源下的服務請求的并發處理能力,而不是縮短了單個服務請求的回應時長,
二、WebClient 的優勢
上文為大家介紹完IO模型之后,我想大家已經可以明白了,與RestTemplate相比,WebClient優勢如下:
- 非阻塞回應式IO,單位時間內有限資源下支持更高的并發量
- 支持使用Java 8 lambda運算式函式
- 同時支持同步、異步與Streaming流式傳輸場景
三、專案引入WebClient
使用WebClient需要引入如下的Jar(可以在包含spring-boot-starter-web的Spring Boot專案中引入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
那么問題又來了,熟悉Spring 開發的朋友應該都知道,spring-boot-starter-webflux和spring-boot-starter-web代表的是兩套技術堆疊
- spring-boot-starter-web可以實作目前比較成熟的基于servlet技術堆疊的Spring Boot應用
- spring-boot-starter-webflux可以實作的是底層基于netty的回應式編程的技術堆疊的Spring Boot應用
二者可以共存么?答案是:
- 作為服務端實作Spring Boot應用而言,二者在應用角度當然是不能共存的,截止20200820我寫稿的時間,如果在一個專案里面將二者都引入了,開發服務端應用其實使用的還是spring-boot-starter-web的基于servlet的技術堆疊,
- 作為HTTP客戶端而言,如果我們只是要使用WebClient,無論怎樣,引入
spring-boot-starter-webflux就對了,
四、WebClient 實體創建與基礎用法
創建WebClient有如下三種方式,我們來一一為大家介紹,
WebClient.create()WebClient.create(String baseUrl):指定了baseUrl,使用該客戶端發送請求都基于baseUrlWebClient.builder()回傳一個WebClient.Builder,該物件可以做鏈式呼叫,傳遞更多的引數,
為了方便后續開發測驗,首先介紹一個網站給大家,JSONPlaceholder是一個提供免費的在線REST API的網站,我們在開發時可以使用它提供的url地址測驗下網路請求以及請求引數,或者當我們程式需要獲取一些模擬資料、模擬圖片時也可以使用它,
4.1. WebClient.create()
創建WebClient發送GET請求,接收String型別單個Mono物件(Mono英文:單聲道、單體),
public class SimpleTest {
@Test
void testSimple() {
WebClient webClient = WebClient.create();
Mono<String> mono = webClient
.get() // 發送GET 請求
.uri("http://jsonplaceholder.typicode.com/posts/1") // 請求路徑
.retrieve() // 獲取回應結果
.bodyToMono(String.class); //回應資料型別轉換
System.out.println("=====" + mono.block());
}
}
mono.block()方法仍然是阻塞式的資料回應接收方式,回應式的編程方法我們后面文章會為大家介紹,

4.2.WebClient.create(String baseUrl)
上面使用create()無參方法,在指定請求uri時每次都要指定完整的HTTP服務路徑,如"http://jsonplaceholder.typicode.com/posts/1",使用WebClient.create(String baseUrl)可以統一指定一個baseUrl,這樣請求指定請求uri時,可以省略baseUrl部分,如"/posts/1",
private WebClient webClient = WebClient.create("http://jsonplaceholder.typicode.com");
@Test
void testBaseUrl(){
Mono<String> mono = webClient
.get()
.uri("/posts/1") // 請求路徑,注意省略了baseurl部分
.retrieve()
.bodyToMono(String.class);
System.out.println("=====" + mono.block());
}
上面代碼請求結果,和4.1結果是一樣的
4.3.WebClient.builder()
使用builder()創建WebClient物件,可以一次性傳遞的引數內容就更加豐富了,cookies、headers等資訊都可以使用builder來傳遞,
場景:比如你請求的服務端使用JWT token,每次請求都需要傳遞token,如果每次請求都單獨去創建一個WebClient,然后指定Token,那就麻煩了,我們可以使用builder在WebClient實體化的時候,統一設定Token,
private WebClient webClient = WebClient
.builder()
.defaultHeader("JWT-Token", "xxxyyy3fsfsfsff-fjdskfa")
.build();
支持的可選配置如下:
uriBuilderFactory: 自定義UriBuilderFactory靈活配置使用UrldefaultHeader: 為HTTP請求設定Headers請求頭defaultCookie: 為HTTP請求設定CookiesdefaultRequest: 自定義Http Requestfilter: 為HTTP請求增加客戶端過濾器exchangeStrategies: HTTP 讀寫資訊自定義clientConnector: HTTP客戶端連接器設定
歡迎關注我的博客,里面有很多精品合集
- 本文轉載注明出處(必須帶連接,不能只轉文字):字母哥博客,
覺得對您有幫助的話,幫我點贊、分享!您的支持是我不竭的創作動力! ,另外,筆者最近一段時間輸出了如下的精品內容,期待您的關注,
- 《手摸手教你學Spring Boot2.0》
- 《Spring Security-JWT-OAuth2一本通》
- 《實戰前后端分離RBAC權限管理系統》
- 《實戰SpringCloud微服務從青銅到王者》
- 《VUE深入淺出系列》
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/63385.html
標籤:Java
下一篇:七種java字串拼接詳解
