在.Net Core應用開發中,調用第三方接口也是常有的事情,HttpClient使用人數、使用頻率算是最高的一種了,在.Net Core中,HttpClient的使用方式隨着版本的升級也發生了一些變化,本次就講解一下Asp.Net Core2.1前后使用的兩種方式。
一、原先HttpClient使用方式
一般來講,喜歡要用的時候才會選擇去獲取資源,因此,當在有需求時才會用HttpClient去調用資源,便會使用如下這種方式或其它方式獲取資源。
//do something... using (var httpClient = new HttpClient()) { var requestUri = "http://aspnetcore.online/api/resource/getresource"; var httpResponseMessage = await httpClient.GetAsync(requestUri); //do something... return Ok(httpResponseMessage); }
如果可以正常訪問目標地址的話,則會返回相應的資源信息。
又如Post方式提交並返回相應的內容,都是可以直接使用。
//do something... using (var httpClient = new HttpClient()) { var requestUri = "http://aspnetcore.online/api/resource/postresource"; var httpResponseMessage = await httpClient.PostAsJsonAsync(requestUri,"星城軟件"); //do something... return Ok(httpResponseMessage); }
但是這種情況下會出現一個嚴重的問題,在不停的調用情形下,tcp連接數會被耗盡,雖然使用using方式調用HttpClient並在退出前調用Dispose()方法將HttpClient釋放了,但是tcp連接仍然處於保持狀態,在240s后才會自動斷開,這里就涉及到一個連接狀態了,首先得理解下http的工作原理,http協議是建立在tcp協議基礎之上,當瀏覽器需要從服務器獲取數據的時候,會發出一次http請求。http會通過tcp建立起一個到服務器的連接通道,當本次請求需要的數據完畢后,http會立即將tcp連接斷開,這個過程是很短的。所以http連接是一種短連接,是一種無狀態的連接。但是tcp的連接只要我們不通過代碼把連接關閉,這個連接就會在客戶端和服務端的進程中一直存在,相關狀態數據會一直保存着,直到無響應狀態持續了默認關閉時間后自動斷開。
當短期請求量過大時,這就可能導致了"套接字資源耗盡異常",因此,為了解決這個問題,想到不釋放HttpClient,將它作為單例一直使用,實現單例方式有很多種。
如使用單例模式,只生成一個HttpClient
private static HttpClient _httpClient = null; public HttpClient CreateHttpClient() { if (_httpClient == null) _httpClient = new HttpClient(); return _httpClient; }
亦或是在初始化時完成單例注入,創建一個IHttpClient接口,及相應的實現StandardHttpClient,實現類種加入HttpClient屬性,在實現類構造函數中完成初始化后便可直接使用該實現類完成資源請求工作。
//在startup中完成單例注入 services.AddSingleton<IHttpClient, StandardHttpClient>(); public interface IHttpClient { //do something } public class StandardHttpClient : IHttpClient { private HttpClient _client; public StandardHttpClient() { _client = new HttpClient(); } //do something... }
雖然這樣解決了"套接字資源耗盡異常",但是又帶來了新的問題,熬不過DNS生存時間(TTL),當主機 DNS 更新時,又可能產生異常,提示無法解析主機名稱,因為單例HttpClient不會隨着主機DNS更新而更新,Singleton HttpClient doesn't respect DNS changes。
An error occurred while sending the request. Couldn't resolve host name An error occurred while sending the request. Couldn't resolve host name
二、現有HttpClient使用方式
在.Net Core2.1后,微軟引入了HttpClientFactory徹底解決這個問題,工廠模式的職責是負責創建對象,這個類主要負責創建HttpClient實例
首先在StartUp中注冊,可能會提示安裝這個Nuget包
services.AddHttpClient();
該方法內部實現過程可以瀏覽:https://www.cnblogs.com/lizhizhang/p/9502862.html
其次,在需要使用時,使用構造函數注入即可
[Route("api/[controller]")] [ApiController] public class HttpClientController : ControllerBase { IHttpClientFactory _httpClientFactory; public HttpClientController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } [HttpGet] [Route(nameof(Index))] public async Task<IActionResult> Index() { var client = _httpClientFactory.CreateClient(); var result = await client.GetAsync("http://aspnetcore.online/api/resource/getresource"); return Ok(result); } }
具體實現原理簡述為:HttpClientFactory內部管理着一個連接句柄池,對每一個HttpClient使用一個句柄進行跟蹤管理,當該實例使用完畢后,句柄仍然控制資源釋放,在短期大量處理時,可以將這部分句柄完成對不同實例的跟蹤管理,使得句柄,也就是相應的套接字生命周期延長,對套接字完成了復用。
近日,長沙.NET技術社區已經建立,微信群:長沙.NET社區一群已滿,如有需要來長沙發展或是回歸長沙可以進入二群,加一下我來邀請進去。各大城市間的人才拉鋸戰進行中,不管是什么行業,什么職業,長沙都應留住人才,培養人才。
2019-02-26,望技術有成后能回來看見自己的腳步