版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第C#中HttpClient使用注意(預(yù)熱與長(zhǎng)連接)最近在測(cè)試一個(gè)第三方API,準(zhǔn)備集成在我們的網(wǎng)站應(yīng)用中。API的調(diào)用使用的是.NET中的HttpClient,由于這個(gè)API會(huì)在關(guān)鍵業(yè)務(wù)中用到,對(duì)調(diào)用API的整體響應(yīng)速度有嚴(yán)格要求,所以對(duì)HttpClient有了格外的關(guān)注。
開始測(cè)試的時(shí)候,只在客戶端通過(guò)HttpClient用PostAsync發(fā)了一個(gè)httppost請(qǐng)求。測(cè)試時(shí)發(fā)現(xiàn),從創(chuàng)建HttpClient實(shí)例,到發(fā)出請(qǐng)求,到讀取到服務(wù)器的響應(yīng)數(shù)據(jù)總耗時(shí)在2s左右,而且多次測(cè)試都是這樣。2s的響應(yīng)速度當(dāng)然是無(wú)法讓人接受的,我們希望至少控制在100ms以內(nèi)。于是開始追查這個(gè)問(wèn)題的原因。
在API的返回?cái)?shù)據(jù)中包含了該請(qǐng)求在服務(wù)端執(zhí)行的耗時(shí),這個(gè)耗時(shí)都在20ms以內(nèi),問(wèn)題與服務(wù)端API無(wú)關(guān)。于是把懷疑點(diǎn)放到了網(wǎng)絡(luò)延遲上,但ping服務(wù)器的響應(yīng)時(shí)間都在10ms左右,網(wǎng)絡(luò)延遲的可能性也不大。
當(dāng)我們正準(zhǔn)備換一個(gè)網(wǎng)絡(luò)環(huán)境進(jìn)行測(cè)試時(shí),突然想到,我們的測(cè)試方式有些問(wèn)題。我們只通過(guò)HttpClient發(fā)了一個(gè)PostAsync請(qǐng)求,假如HttpClient在第一次調(diào)用時(shí)存在某種預(yù)熱機(jī)制(比如在EF中就有這樣的機(jī)制),現(xiàn)在2s的總耗時(shí)可能大多消耗在HttpClient的預(yù)熱上。
于是修改測(cè)試代碼,將調(diào)用由1次改為100次,然后恍然大悟地發(fā)現(xiàn)只有第1次是2s,接下來(lái)的99次都在100ms以內(nèi)。果然是HttpClient的某種預(yù)熱機(jī)制在搞鬼!
既然知道了是HttpClient預(yù)熱機(jī)制的原因,那我們可以幫HttpClient進(jìn)行熱身,減少第一次請(qǐng)求的耗時(shí)。我們嘗試了一種預(yù)熱方式,在正式發(fā)httppost請(qǐng)求之前,先發(fā)一個(gè)httphead請(qǐng)求,代碼如下:
_httpClient.SendAsync(newHttpRequestMessage{
Method=newHttpMethod("HEAD"),
RequestUri=newUri(BASE_ADDRESS+"/")})
.Result.EnsureSuccessStatusCode();
經(jīng)測(cè)試,通過(guò)這種熱身方法,可以將第一次請(qǐng)求的耗時(shí)由2s左右降到1s以內(nèi)(測(cè)試結(jié)果是700多ms)。
在知道第1次HttpClient請(qǐng)求耗時(shí)2s的真相之后,我們將目光轉(zhuǎn)向了剩下的99次耗時(shí)100ms以內(nèi)的請(qǐng)求,發(fā)現(xiàn)絕大部分請(qǐng)求都在50ms以上。有沒(méi)有可能將之降至50ms以下?而且,之前一直有這樣的糾結(jié):每次調(diào)用是不是一定要對(duì)HttpClient進(jìn)行Dispose()?是不是要將HttpClient單例或者靜態(tài)化(聲明為靜態(tài)變量)?借此機(jī)會(huì)一起研究一下。
在HttpClient的背后,有一個(gè)對(duì)請(qǐng)求響應(yīng)速度有著不容忽視影響的東東TCP連接。一個(gè)HttpClient實(shí)例會(huì)關(guān)聯(lián)一個(gè)TCP連接,在對(duì)HttpClient進(jìn)行Dispose時(shí),會(huì)關(guān)閉TCP連接(我們用Wireshark進(jìn)行網(wǎng)絡(luò)抓包也驗(yàn)證了這一點(diǎn))。
在之前的測(cè)試中,我們每次用HttpClient發(fā)請(qǐng)求時(shí),都是新建一個(gè)HttpClient實(shí)例,用完就對(duì)它進(jìn)行Dispose,代碼如下:
using(varhttpClient=newHttpClient(){BaseAddress=newUri(BASE_ADDRESS)})
httpClient.PostAsync("/",newFormUrlEncodedContent(parameters));
}
所以每次請(qǐng)求時(shí)都要經(jīng)歷新建TCP連接-傳數(shù)據(jù)-關(guān)閉連接(也就是通常所說(shuō)的短連接),而且雪上加霜的是請(qǐng)求用的是https,建立TCP連接時(shí)還需要一個(gè)基于公私鑰加解密的keyexchange過(guò)程:ClientHello-ServerHello-Certificate-ClientKeyExchange-NewSessionTicket。
如果我們想將請(qǐng)求響應(yīng)時(shí)間降至50ms以下,就必須從這個(gè)地方下手重用TCP連接(也就是通常所說(shuō)的長(zhǎng)連接)。要實(shí)現(xiàn)長(zhǎng)連接,首先需要的就是在HttpClient第1次請(qǐng)求后不關(guān)閉TCP連接(不調(diào)用Dispose方法);而要讓后續(xù)的請(qǐng)求繼續(xù)使用這個(gè)未關(guān)閉的TCP連接,我們必須要使用同一個(gè)HttpClient實(shí)例;而要使用同一個(gè)HttpClient實(shí)例,就得實(shí)現(xiàn)HttpClient的單例或者靜態(tài)化。之前的3個(gè)問(wèn)題,由于要解決第1個(gè)問(wèn)題,后2個(gè)問(wèn)題變成了別無(wú)選擇。
為了實(shí)現(xiàn)長(zhǎng)連接,我們將HttpClient的調(diào)用代碼改為如下的樣子:
然后測(cè)試一下請(qǐng)求響應(yīng)時(shí)間:
Elapsed:750ms
Elapsed:31ms
Elapsed:30ms
Elapsed:43ms
Elapsed:27ms
Elapsed:29ms
Elapsed:28ms
Elapsed:35ms
Elapsed:36ms
Elapsed:31ms
....
除了第1次請(qǐng)求,接下來(lái)的99次請(qǐng)求絕大多數(shù)都在50ms以內(nèi)。TCP長(zhǎng)連接的效果必須的!
通過(guò)Wireshak抓包也驗(yàn)證了長(zhǎng)連接的效果:
Wireshak抓包
這時(shí),你
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();
}
}
也許會(huì)產(chǎn)生這樣的疑問(wèn):將HttpClient聲明為靜態(tài)變量,會(huì)不會(huì)存在線程安全問(wèn)題?我們當(dāng)時(shí)也有這樣的疑問(wèn),后來(lái)在stackoverflow上找到了答案:
Asperthecommentsbelow(thanks@ischell),thefollowinginstancemethodsarethreadsafe(allasync):
CancelPendingRequests
DeleteAsync
GetAsync
GetByteArrayAsync
GetStreamAsync
GetStringAsync
PostAsync
PutAsync
SendAsync
HttpClient的所有異步方法都是線程安全的,放心使用。
到這里,HttpClient的問(wèn)題是不是可以完美收官了?。。。稍等,還有一個(gè)問(wèn)題。
客戶端雖然保持著TCP連接,但TCP連接是兩口子的事,服務(wù)器端呢?你不告訴服務(wù)器,服務(wù)器怎么知道你要一直保持TCP連接呢?對(duì)于客戶端,保持TCP連接的開銷不大;但是對(duì)于服務(wù)器,則完全不一樣的,如果默認(rèn)都保持TCP連接,那可是要保持成千上萬(wàn)客戶端的連接啊。所以,一般的Web服務(wù)器都會(huì)根據(jù)客戶端的訴求來(lái)決定
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 銀行柜員服務(wù)禮儀與風(fēng)險(xiǎn)防范手冊(cè)
- 干擾素α聯(lián)合PD-1單抗治療非小細(xì)胞肺癌的療效與機(jī)制探究
- 常規(guī)與非體外循環(huán)冠狀動(dòng)脈旁路移植術(shù)療效的系統(tǒng)剖析:基于Meta分析的洞察
- 餐廳食品安全
- 施工現(xiàn)場(chǎng)臨時(shí)用電安全技術(shù)措施及監(jiān)控
- 四年級(jí)口語(yǔ)交際課堂教學(xué)設(shè)計(jì)實(shí)例
- 工廠防火安全管理措施
- 中小企業(yè)財(cái)務(wù)預(yù)算制定與控制方法
- 帶供熱機(jī)組直接空冷系統(tǒng):安全、經(jīng)濟(jì)與設(shè)備檢修質(zhì)量管理的深度剖析
- 分布式存儲(chǔ)架構(gòu)優(yōu)化-第2篇
- 工程維保三方合同
- 地鐵車輛檢修安全培訓(xùn)
- 造血干細(xì)胞移植臨床應(yīng)用和新進(jìn)展課件
- GB/T 10802-2023通用軟質(zhì)聚氨酯泡沫塑料
- 黑布林英語(yǔ)閱讀初一年級(jí)16《柳林風(fēng)聲》譯文和答案
- 杰青優(yōu)青學(xué)術(shù)項(xiàng)目申報(bào)答辯PPT模板
- 宿舍入住申請(qǐng)書
- 深圳中核海得威生物科技有限公司桐城分公司碳13-尿素原料藥項(xiàng)目環(huán)境影響報(bào)告書
- 2023年全國(guó)高考體育單招文化考試數(shù)學(xué)試卷真題及答案
- GB/T 28733-2012固體生物質(zhì)燃料全水分測(cè)定方法
- GB/T 14404-2011剪板機(jī)精度
評(píng)論
0/150
提交評(píng)論