最近在測試乙個第三方api,準備整合在我們的**應用中。api的呼叫使用的是.net中的httpclient,由於這個api會在關鍵業務中用到,對呼叫api的整體響應速度有嚴格要求,所以對httpclient有了格外的關注。
開始測試的時候,只在客戶端通過httpclient用postasync發了乙個http post請求。測試時發現,從建立httpclient例項,到發出請求,到讀取到伺服器的響應資料總耗時在2s左右,而且多次測試都是這樣。2s的響應速度當然是無法讓人接受的,我們希望至少控制在100ms以內。於是開始追查這個問題的原因。
在api的返回資料中包含了該請求在服務端執行的耗時,這個耗時都在20ms以內,問題與服務端api無關。於是把懷疑點放到了網路延遲上,但ping伺服器的響應時間都在10ms左右,網路延遲的可能性也不大。
當我們正準備換乙個網路環境進行測試時,突然想到,我們的測試方式有些問題。我們只通過httpclient發了乙個postasync請求,假如httpclient在第一次呼叫時存在某種預熱機制(比如在ef中就有這樣的機制),現在2s的總耗時可能大多消耗在httpclient的預熱上。
於是修改測試**,將呼叫由1次改為100次,然後恍然大悟地發現——只有第1次是2s,接下來的99次都在100ms以內。果然是httpclient的某種預熱機制在搞鬼!
既然知道了是httpclient預熱機制的原因,那我們可以幫httpclient進行熱身,減少第一次請求的耗時。我們嘗試了一種預熱方式,在正式發http post請求之前,先發乙個http head請求,**如下:
經測試,通過這種熱身方法,可以將第一次請求的耗時由2s左右降到1s以內(測試結果是700多ms)。
在知道第1次httpclient請求耗時2s的真相之後,我們將目光轉向了剩下的99次耗時100ms以內的請求,發現絕大部分請求都在50ms以上。有沒有可能將之降至50ms以下?而且,之前一直有這樣的糾結:每次呼叫是不是一定要對httpclient進行dispose()?是不是要將httpclient單例或者靜態化(宣告為靜態變數)?藉此機會一起研究一下。
在httpclient的背後,有乙個對請求響應速度有著不容忽視影響的東東——tcp連線。乙個httpclient例項會關聯乙個tcp連線,在對httpclient進行dispose時,會關閉tcp連線(我們用wireshark進行網路抓包也驗證了這一點)。
在之前的測試中,我們每次用httpclient發請求時,都是新建乙個httpclient例項,用完就對它進行dispose,**如下:
所以每次請求時都要經歷新建tcp連線->傳資料->關閉連線(也就是通常所說的短連線),而且雪上加霜的是請求用的是https,建立tcp連線時還需要乙個基於公私鑰加解密的key exchange過程:client hello -> server hello -> certificate -> client key exchange -> new session ticket。
如果我們想將請求響應時間降至50ms以下,就必須從這個地方下手——重用tcp連線(也就是通常所說的長連線)。要實現長連線,首先需要的就是在httpclient第1次請求後不關閉tcp連線(不呼叫dispose方法);而要讓後續的請求繼續使用這個未關閉的tcp連線,我們必須要使用同乙個httpclient例項;而要使用同乙個httpclient例項,就得實現httpclient的單例或者靜態化。之前的3 個問題,由於要解決第1個問題,後2個問題變成了別無選擇。
為了實現長連線,我們將httpclient的呼叫**改為如下的樣子:
; //幫httpclient熱身
除了第1次請求,接下來的99次請求絕大多數都在50ms以內。tcp長連線的效果必須的!
通過wireshak抓包也驗證了長連線的效果:
httpclient的所有非同步方法都是執行緒安全的,放心使用。
到這裡,httpclient的問題是不是可以完美收官了?。。。稍等,還有乙個問題。
客戶端雖然保持著tcp連線,但tcp連線是兩口子的事,伺服器端呢?你不告訴伺服器,伺服器怎麼知道你要一直保持tcp連線呢?對於客戶端,保持tcp連線的開銷不大;但是對於伺服器,則完全不一樣的,如果預設都保持tcp連線,那可是要保持成千上萬客戶端的連線啊。所以,一般的web伺服器都會根據客戶端的訴求來決定是否保持tcp連線,這就是keep-alive存在的理由。
所以,我們還要給httpclient增加乙個connection:keep-alive的請求頭,**如下:
_httpclient.defaultrequestheaders.connection.add("keep-alive");
現在終於可以收官了。但是肯定不完美,分享的只是解決問題的過程。 C 中 HttpClient 的簡單使用
當我們在開發客戶端還有少部分服務端程式時,資料需要從 webapi 伺服器取得。在以前我使用的比較多的時 webclient 和 httpwebrequese,在新的 net 版本中,團隊為其帶來了更多的可選擇性 新的 httpclient。它有著更加靈活的 api。傳送請求和接收響應主要是通過 h...
C 中HttpClient的使用中同步非同步問題
專案中遇到了這樣的問題 第一次 httpresponsemessage response await httpclient.postasync url,null 傳送了乙個post非同步請求 操作了乙個datatable的資料 第二次 又發了乙個post非同步請求,用到了第一步中的datatable...
C 中HttpClient的使用中同步非同步問題
專案中遇到了這樣的問題 第一次 httpresponsemessage response await httpclient.postasync url,null 傳送了乙個post非同步請求 操作了乙個datatable的資料 第二次 又發了乙個post非同步請求,用到了第一步中的datatable...