C#中HttpClient使用注意(預熱與長連接)_第1頁
C#中HttpClient使用注意(預熱與長連接)_第2頁
C#中HttpClient使用注意(預熱與長連接)_第3頁
C#中HttpClient使用注意(預熱與長連接)_第4頁
C#中HttpClient使用注意(預熱與長連接)_第5頁
已閱讀5頁,還剩1頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

第C#中HttpClient使用注意(預熱與長連接)最近在測試一個第三方API,準備集成在我們的網(wǎng)站應用中。API的調(diào)用使用的是.NET中的HttpClient,由于這個API會在關鍵業(yè)務中用到,對調(diào)用API的整體響應速度有嚴格要求,所以對HttpClient有了格外的關注。

開始測試的時候,只在客戶端通過HttpClient用PostAsync發(fā)了一個httppost請求。測試時發(fā)現(xiàn),從創(chuàng)建HttpClient實例,到發(fā)出請求,到讀取到服務器的響應數(shù)據(jù)總耗時在2s左右,而且多次測試都是這樣。2s的響應速度當然是無法讓人接受的,我們希望至少控制在100ms以內(nèi)。于是開始追查這個問題的原因。

在API的返回數(shù)據(jù)中包含了該請求在服務端執(zhí)行的耗時,這個耗時都在20ms以內(nèi),問題與服務端API無關。于是把懷疑點放到了網(wǎng)絡延遲上,但ping服務器的響應時間都在10ms左右,網(wǎng)絡延遲的可能性也不大。

當我們正準備換一個網(wǎng)絡環(huán)境進行測試時,突然想到,我們的測試方式有些問題。我們只通過HttpClient發(fā)了一個PostAsync請求,假如HttpClient在第一次調(diào)用時存在某種預熱機制(比如在EF中就有這樣的機制),現(xiàn)在2s的總耗時可能大多消耗在HttpClient的預熱上。

于是修改測試代碼,將調(diào)用由1次改為100次,然后恍然大悟地發(fā)現(xiàn)只有第1次是2s,接下來的99次都在100ms以內(nèi)。果然是HttpClient的某種預熱機制在搞鬼!

既然知道了是HttpClient預熱機制的原因,那我們可以幫HttpClient進行熱身,減少第一次請求的耗時。我們嘗試了一種預熱方式,在正式發(fā)httppost請求之前,先發(fā)一個httphead請求,代碼如下:

_httpClient.SendAsync(newHttpRequestMessage{

Method=newHttpMethod("HEAD"),

RequestUri=newUri(BASE_ADDRESS+"/")})

.Result.EnsureSuccessStatusCode();

經(jīng)測試,通過這種熱身方法,可以將第一次請求的耗時由2s左右降到1s以內(nèi)(測試結(jié)果是700多ms)。

在知道第1次HttpClient請求耗時2s的真相之后,我們將目光轉(zhuǎn)向了剩下的99次耗時100ms以內(nèi)的請求,發(fā)現(xiàn)絕大部分請求都在50ms以上。有沒有可能將之降至50ms以下?而且,之前一直有這樣的糾結(jié):每次調(diào)用是不是一定要對HttpClient進行Dispose()?是不是要將HttpClient單例或者靜態(tài)化(聲明為靜態(tài)變量)?借此機會一起研究一下。

在HttpClient的背后,有一個對請求響應速度有著不容忽視影響的東東TCP連接。一個HttpClient實例會關聯(lián)一個TCP連接,在對HttpClient進行Dispose時,會關閉TCP連接(我們用Wireshark進行網(wǎng)絡抓包也驗證了這一點)。

在之前的測試中,我們每次用HttpClient發(fā)請求時,都是新建一個HttpClient實例,用完就對它進行Dispose,代碼如下:

using(varhttpClient=newHttpClient(){BaseAddress=newUri(BASE_ADDRESS)})

httpClient.PostAsync("/",newFormUrlEncodedContent(parameters));

}

所以每次請求時都要經(jīng)歷新建TCP連接-傳數(shù)據(jù)-關閉連接(也就是通常所說的短連接),而且雪上加霜的是請求用的是https,建立TCP連接時還需要一個基于公私鑰加解密的keyexchange過程:ClientHello-ServerHello-Certificate-ClientKeyExchange-NewSessionTicket。

如果我們想將請求響應時間降至50ms以下,就必須從這個地方下手重用TCP連接(也就是通常所說的長連接)。要實現(xiàn)長連接,首先需要的就是在HttpClient第1次請求后不關閉TCP連接(不調(diào)用Dispose方法);而要讓后續(xù)的請求繼續(xù)使用這個未關閉的TCP連接,我們必須要使用同一個HttpClient實例;而要使用同一個HttpClient實例,就得實現(xiàn)HttpClient的單例或者靜態(tài)化。之前的3個問題,由于要解決第1個問題,后2個問題變成了別無選擇。

為了實現(xiàn)長連接,我們將HttpClient的調(diào)用代碼改為如下的樣子:

然后測試一下請求響應時間:

Elapsed:750ms

Elapsed:31ms

Elapsed:30ms

Elapsed:43ms

Elapsed:27ms

Elapsed:29ms

Elapsed:28ms

Elapsed:35ms

Elapsed:36ms

Elapsed:31ms

....

除了第1次請求,接下來的99次請求絕大多數(shù)都在50ms以內(nèi)。TCP長連接的效果必須的!

通過Wireshak抓包也驗證了長連接的效果:

Wireshak抓包

這時,你

publicclassHttpClientTest

privatestaticreadonlyHttpClient_httpClient;

staticHttpClientTest()

{

_httpClient=newHttpClient(){BaseAddress=newUri(BASE_ADDRESS)};

//幫HttpClient熱身

_httpClient.SendAsync(newHttpRequestMessage{

Method=newHttpMethod("HEAD"),

RequestUri=newUri(BASE_ADDRESS+"/")})

.Result.EnsureSuccessStatusCode();

}

publicasyncTaskstringPostAsync()

{

varresponse=await_httpClient.PostAsync("/",newFormUrlEncodedContent(parameters));

returnawaitresponse.Content.ReadAsStringAsync();

}

}

也許會產(chǎn)生這樣的疑問:將HttpClient聲明為靜態(tài)變量,會不會存在線程安全問題?我們當時也有這樣的疑問,后來在stackoverflow上找到了答案:

Asperthecommentsbelow(thanks@ischell),thefollowinginstancemethodsarethreadsafe(allasync):

CancelPendingRequests

DeleteAsync

GetAsync

GetByteArrayAsync

GetStreamAsync

GetStringAsync

PostAsync

PutAsync

SendAsync

HttpClient的所有異步方法都是線程安全的,放心使用。

到這里,HttpClient的問題是不是可以完美收官了?。。。稍等,還有一個問題。

客戶端雖然保持著TCP連接,但TCP連接是兩口子的事,服務器端呢?你不告訴服務器,服務器怎么知道你要一直保持TCP連接呢?對于客戶端,保持TCP連接的開銷不大;但是對于服務器,則完全不一樣的,如果默認都保持TCP連接,那可是要保持成千上萬客戶端的連接啊。所以,一般的Web服務器都會根據(jù)客戶端的訴求來決定

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論