使用Apache的HttpClient發送Http請求

使用Apache的HttpClient發送Http請求

1 基礎概念

1.1 HttpClient、TCP/IP、Socket的區別

HttpClient是Apache中一個開源的項目。它實現了HTTP標準中Client端的所有功能,使用它能夠很容易的進行HTTP信息的傳輸。

網絡從下往上分為:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。

TCP/IP協議:主要解決的是數據傳輸層面的事情,如果沒有規范的應用協議,數據能從網絡里的A節點傳到B節點,但是無法有效識別,建立在TCP/IP上的應用協議很多,比如:rpc、ftp等。所以,無論應用協議多強大,最終都要依靠傳輸層協議進行數據傳輸。

Http、Https:主要解決的是數據規范層面的事情,作用于應用層。而HTTPS是以安全為目標的HTTP通道,簡單講是HTTP的安全版,即HTTP下加入SSL層。

Socket:是tcp/ip的一個編程實現,在程序里,Http請求最終一定要綁定到一個具體的Socket連接進行上行和下行傳輸。Socket本身并不是協議,而是一個調用接口(API)。

1.2 HttpClient的介紹

對于簡單應用,HttpURLConnection完全可以滿足。但是對于

1)系統復雜度高,

2)性能要求高,

3)可靠性要求也高的應用,

則需要一個更強大的組件。HttpClient可以滿足這些條件。

2 HttpClient的使用

2.1 創建一個HttpClient對象

創建HttpClient對象的方式是使用CloseableHttpClient的builder類的HttpClientBuilder,先對一些屬性進行配置(采用裝飾者模式,不斷的setXxx().setXxx() .. 就行),再調用build方法來創建實例。

public InternalHttpClient(ClientExecChain execChain,
 HttpClientConnectionManager connManager, 
 HttpRoutePlanner routePlanner, 
 Lookup<CookieSpecProvider> cookieSpecRegistry,
 Lookup<AuthSchemeProvider> authSchemeRegistry, 
 CookieStore cookieStore, 
 CredentialsProvider credentialsProvider, 
 RequestConfig defaultConfig, 
 List<Closeable> closeables) {
 Args.notNull(execChain, "HTTP client exec chain");
 Args.notNull(connManager, "HTTP connection manager");
 Args.notNull(routePlanner, "HTTP route planner");
 this.execChain = execChain;
 this.connManager = connManager;
 this.routePlanner = routePlanner;
 this.cookieSpecRegistry = cookieSpecRegistry;
 this.authSchemeRegistry = authSchemeRegistry;
 this.cookieStore = cookieStore;
 this.credentialsProvider = credentialsProvider;
 this.defaultConfig = defaultConfig;
 this.closeables = closeables;
}

需要關注的幾個重要的初始化配置如下: HttpCLientConnectionManager、RequestConfig

2.2 創建RequestConfig

RequestConfig是對Request的一些配置,里面比較重要的有三個超時時間,默認情況下這三個時間都是0(如果不設置request的config,會在execute的過程中使用HttpClientParamConfig的getRequestConfig中使用默認參數進行設置,默認設置為-1),這也就意味著無限等待,很容易導致所有的請求阻塞在這個地方無限期等待。

這三個超時時間為:

A :connectionRequestTimeout—從連接池中取連接的超時時間

這個時間定義的是從ConnectionManager管理的連接池中取出連接的超時時間,如果連接池中沒有可用的連接,則request會被阻塞,最長等待connectionRequestTimeout的時間,如果還沒有服務,則拋出ConnectionPoolTimeoutException異常,不繼續等待。

B :connectTimeout—連接超時時間

這個時間定義了通過網絡與服務器建立連接的超時時間,也就是取得了連接池中某個連接之后接通目標url等待時間,會發生超時,拋出ConnectionTimeoutException異常。

C :socketTimeout—請求超時時間

這個時間定義了socket讀數據的超時時間,也就是連接到服務器之后到從服務器獲取響應數據需要等待的時間,或者說是連接上一個url之后到獲取response的返回等待時間。發生超時,會拋出SocketTimeoutException異常。

b38865f401418f8a.png

2.3 創建HttpClientConnectionManager

HttpClientConnectionManager是一個HTTP連接管理器,它負責新的HTTP連接的創建、管理連接的生命周期和保證一個HTTP連接在某一時刻只被一個線程使用。

關鍵的概念——Route

在HttpClient中,一個Route指運行環境機器 --> 目標機器(域名)的一條線路,也就是如果目標url的域名是同一個,那么它們的Route也是一樣的。

6884225c907714f2.png

HttpClientConnectionManager有兩種具體實現:

A : BasicHttpClientConnectionManager:每次只管理一個connection,但由于它只管理一個連接,所以只能被一個線程使用,它在管理連接的時候如果發現有相同的Route請求,會復用之前已經創建的連接,如果新來的請求不能復用之前的連接,它會關閉現有連接并重新打開它來相應新的請求。

B:PoolingHttpClientConnectionManager:與A不同(這個是默認設置),它管理著一個連接池,可以同時為多個線程服務,每次新來一個請求,如果在連接池中已經存在Route相同并且可用的connection,連接池就會直接復用這個connection;當不存在Route相同的connection,就新建一個connection為之服務;如果連接池已滿,則請求會等待直到被服務或者超時。

整個PoolingHttpClientConnectionManager生命周期從HttpClient初始化開始到整個應用結束調用httpClient.close()。 在PoolingHttpClientConnectionManager的配置中有兩個最大連接數量,分別控制著總的最大連接數量和每個route的最大連接數量。如果沒有顯式設置,默認每個route只允許最多2個connection,總的connection數量不超過20。

b924831b9ed63d6e.png

2.4 創建一個Request對象以及執行Request請求

HttpClient支持所有HTTP1.1中所有定義的請求類型:GET、HEAD、POST、PUT、DELETE、TRACE和OPTIONS。對使用的類為HttpGet、HttpHead、HttpPost、HttpPut、HttpDelete、HttpTrace和HttpOptions。Request的對象建立很簡單,一般用目標url來構造就好了。下面是一個HttpPost的創建代碼:

HttpPost httpPost = new HttpPost(url);

執行request請求的方式是:

httpclient.execute(HttpUriRequest request)

重點強調的是:HttpClient允許http連接在特定的Http上下文中執行,HttpContext是跟一個連接相關聯的,所以它也只能屬于一個線程,如果沒有特別設定,在execute的過程中,HttpClient會自動為每一個connection new一個HttpClientHttpContext。

final HttpClientContext localContext 
 = HttpClientContext.adapt(context != null ? context : new BasicHttpContext);

整個執行execute的過程如下:

e6d9a9c3f447283a.png

3 重點關注

3.1 連接池管理

連接池的獲取的完整邏輯是:在每收到一個route請求后,連接池都會建立一個以這個route為key的子連接池,當有一個新的連接請求到來的時候,它會優先匹配已經存在的子連接池們,如果之前已經有過以這個route為key的子連接池,那么就會去試圖取這個子連接池中狀態為available的連接,如果此時有可用的連接,則將取得的available連接狀態改為leased的,取連接成功。 如果此時子連接池沒有可用連接,那再看是否達到了所設置的最大連接數和每個route所允許的最大連接數的上限,如果還有余量則new一個新的連接,或者取得lastUsedConnection,關閉這個連接、把連接從原來所在的子連接池刪除,再lease取連接成功。 如果此時的情況不允許再new一個新的連接,就把這個請求連接的請求放入一個queue中排隊等待,直到得到一個連接或者超時才會從queue中刪去。

2e2aea7d6967b6b3.png

3.2 連接池回收

目前連接池的回收機制有二種:

第一種設置 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); httpClientBuilder.evictExpiredConnections();//制定過期連接,httpClientBuilder.evictIdleConnections(60, TimeUnit.SECONDS); 用來關閉閑置連接,它會啟動一個守護線程進行清理工作。用戶可以通過builder#evictIdleConnections開啟該組件,并通過builder#setmaxIdleTime設置最大空閑時間。

第二種設置:初始化PoolingHttpClientConnectionManager設置每個連接的生命周期 PoolingHttpClientConnectionManager connManager =new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, 360, TimeUnit.SECONDS);

3.3 keep-alive

在HttpClient.execute得到response之后的相關代碼中,它會先取出response的keep-alive頭來設置connection是否resuable以及存活的時間。如果服務器返回的響應中包含了Connection:Keep-Alive(默認有的),但沒有包含Keep-Alive時長的頭消息,HttpClient認為這個連接可以永遠保持,也就是長連接。

3.4 常見問題

有時會出現線程阻塞,經過上面的介紹,主要原因是:

一:沒有增加連接池的回收機制,造成長連接一直保持。

二:未設置connectionRequestTimeout,造成請求在連接池中一直等待,未在服務器中拋出異常。

4 小結

本篇文章簡單介紹了發送Http請求——Apache的HttpClient,由于純手打,難免會有紕漏,如果發現錯誤的地方,請第一時間告訴我,這將是我進步的一個很重要的環節。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容