C#中HttpClient使用注意:預熱與長串連

來源:互聯網
上載者:User

標籤:

原文地址:C#中HttpClient使用注意:預熱與長串連

最近在測試一個第三方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請求,代碼如下:

_httpClient.SendAsync(new HttpRequestMessage {                    Method = new HttpMethod("HEAD"),                     RequestUri = new Uri(BASE_ADDRESS + "/") })                .Result.EnsureSuccessStatusCode();

經測試,通過這種熱身方法,可以將第一次請求的耗時由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,代碼如下:

using (var httpClient = new HttpClient() { BaseAddress = new Uri(BASE_ADDRESS) }){    httpClient.PostAsync("/", new FormUrlEncodedContent(parameters));}

所以每次請求時都要經曆建立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的調用代碼改為如下的樣子:

public class HttpClientTest{     private static readonly HttpClient _httpClient;    static HttpClientTest()    {        _httpClient = new HttpClient() { BaseAddress = new Uri(BASE_ADDRESS) };        //幫HttpClient熱身        _httpClient.SendAsync(new HttpRequestMessage {                Method = new HttpMethod("HEAD"),                 RequestUri = new Uri(BASE_ADDRESS + "/") })            .Result.EnsureSuccessStatusCode();    }    public async Task<string> PostAsync()    {        var response = await _httpClient.PostAsync("/", new FormUrlEncodedContent(parameters));        return await response.Content.ReadAsStringAsync();    }}

然後測試一下請求回應時間:

  Elapsed:750ms  Elapsed:31ms  Elapsed:30ms  Elapsed:43ms  Elapsed:27ms  Elapsed:29ms  Elapsed:28ms  Elapsed:35ms  Elapsed:36ms  Elapsed:31ms  ....

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

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

這時,你也許會產生這樣的疑問:將HttpClient聲明為靜態變數,會不會存線上程安全問題?我們當時也有這樣的疑問,後來在stackoverflow上找到了答案:

As per the comments below (thanks @ischell), the following instance methods are thread safe (all async):CancelPendingRequestsDeleteAsyncGetAsyncGetByteArrayAsyncGetStreamAsyncGetStringAsyncPostAsyncPutAsyncSendAsync

HttpClient的所有非同步方法呼叫都是安全執行緒的,放心使用。

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

用戶端雖然保持著TCP串連,但TCP串連是兩口子的事,伺服器端呢?你不告訴伺服器,伺服器怎麼知道你要一直保持TCP串連呢?對於用戶端,保持TCP串連的開銷不大;但是對於伺服器,則完全不一樣的,如果預設都保持TCP串連,那可是要保持成千上萬用戶端的串連啊。所以,一般的Web伺服器都會根據用戶端的訴求來決定是否保持TCP串連,這就是keep-alive存在的理由。

所以,我們還要給HttpClient增加一個Connection:keep-alive的要求標頭,代碼如下:

_httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");

現在終於可以收官了。但是肯定不完美,分享的只是解決問題的過程。

C#中HttpClient使用注意:預熱與長串連

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.