ASP.NET Core 2.1中出現一個新的HttpClientFactory功能,git
它有助於解決開發人員在使用HttpClient實例從其應用程序發出外部Web請求時可能遇到的一些常見問題。github
在.NETCore平臺的2.1新增了HttpClientFactory,雖然HttpClient這個類實現了disposable,但使用它的時候用聲明using包裝塊的方式
一般不是最好的選擇。處理HttpClient
,底層socket套接字不會當即釋放。該HttpClient類
是專爲多個請求重複使用而建立的。須要不一樣的基地址,不一樣的HTTP標頭和其餘對請求個性化操做的場景時,須要手動管理多個HttpClient實例,爲了簡化HttpClient
實例管理,.NET Core 2.1提供了一個新的HTTPClientFactory - 它能夠建立,緩存和處理HttpClient
實例。
緩存
用ASP.NET團隊的話說:「an opinionated factory for creating HttpClient instances」(一個用於建立HttpClient實例的最佳實踐的工廠),而且是ASP.NET Core 2.1發佈的新功能。根據你們之前使用HttpClient的經驗,您可能遇到一些困擾的問題,有時甚至沒有意識到您有問題(只是在併發並不大的場景沒觸發而已)。安全
第一個問題是當你在代碼中建立太多的HttpClients時,這反過來會產生兩個問題......服務器
HttpClient實現了IDisposable,這一般會致使開發人員在使用IDisposable對象時遵循正常模式,在using塊中建立它。這樣能夠確保一旦完成對象而且它已經超出範圍,就能夠正確銷燬對象。併發
所以,最優的方法是重用HttpClient實例,以便也能夠重用鏈接。HttpClient是一個可變對象,但只要你沒有運行期改變它,它其實是線程安全的而且能夠共享。所以,一種常見的方法是將其註冊爲具備DI框架的單例模式,或者建立包含static靜態實例的對象。app
可是,這會產生新問題。以這種方式使用單個HttpClient將保持鏈接打開而且不遵照DNS生存時間(TTL)設置(總之就是同一個HttpClient實例只能有一個請求頭,在被請求方發生更改時,因爲是單例不能作個性化改變,不然致使其餘請求失敗)。如今鏈接將永遠不會得到DNS更新,所以您正在與之通訊的服務器將永遠不會更新其地址。在某些狀況下,這是徹底有可能的,在以上這種狀況下,您能夠平衡許多主機,這些主機可能隨着時間的推移而改變,或者可能使用Blue/Green 部署推出新服務。若是服務器消改變,則您的鏈接使用的IP可能再也不響應您經過單個HttpClient發出的請求。框架
因此須要咱們手動去管理每類服務器的HttpClient的實例來進行個性化請求頭的構造和發起請求!
socket
HttpClientFactory旨在幫助您開始解決這些問題,並提供了一種新的機制來建立在幕後爲咱們正確管理的HttpClient實例。它將爲咱們「作管理HttpClient的事」,咱們能夠專一於業務!雖然在參考HttpClient時提到了上述問題,但事實上問題的根源實際上發生在HttpClient上,HttpClient使用了HttpClientHandler。HttpClientFactory管理處理程序的生命週期,以便咱們有一個能夠重用的池,同時還能夠(Rotating)輪換它們以使DNS不會過期。url
使用HttpClient的昂貴部分其實是建立HttpClientHandler和鏈接。以這種HttpClientFacotry方式聚集這些內容意味着咱們能夠更高效利用資源最節省地使用咱們系統上的socket。當您使用HttpClientFactory請求HttpClient時,實際上每次都會得到一個新實例,這意味着咱們沒必要擔憂會改變它的狀態。此HttpClient可能(或可能不)使用池中的現有HttpClientHandler,從而使用現有打開的鏈接。
默認狀況下,每一個新建立的HttpClientHandler(派生自HttpMessageHandler)生命週期只有2分鐘。經過services.AddHttpClient()建立HttpClientFactory實例時,能夠根據每個命名的Client客戶機進行控制。達到生命週期後,處理程序將不會當即被釋放掉,而是放入過時的池中。任何依賴於HttpClientFactory的處理程序鏈的客戶端均可以繼續使用它而沒有任何問題。有一個後臺做業檢查過時的池,以查看處理程序的全部引用是否已在scope以外,此時能夠將其釋放掉。處理程序鏈過時後對新客戶端的任何新請求都將得到新的處理程序鏈。
這種方法運行得至關不錯,但.NET Core方面還有其餘一些事情可能會進一步改善這種狀況。.NET Core團隊開發了一個新的ManagedHandler,它能夠更正確地管理DNS,原則上能夠保持更長時間,這意味着能夠更有效地共享鏈接。這個新的處理程序還被設計爲在不一樣的操做系統中更加一致地運行。在該工做完成以前,上面的處理程序池是一個合理的解決方法。
咱們將首先建立一個簡單的WebAPI項目
接下來,咱們須要轉到咱們的Startup.cs文件並註冊一個服務。
services.AddHttpClient();
services.AddScoped(typeof(ClassInService));//此處無關HttpClient,請暫時忽視他
在幕後,這將註冊一些必需的服務,其中一個是IHttpClientFactory的實現。接下來,咱們在業務中使用他
public class ClassInService { /// <summary> /// 構建器 /// </summary> /// <param name="clientFactory"></param> public ClassInService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } }
private void HttpClientFactoryTest() { var client = _clientFactory.CreateClient("這是專門用來鏈接博客園的");//必須和services.AddHttpClient()中指定的名稱對應 var content = new StringContent($"SID={SID}&safeKey={111}"); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var response = client.PostAsync("MyBlogUrl", content); }
這裏咱們首先添加對IHttpClientFactory的依賴,它將由DI系統注入ClassInService。IHttpClientFactory容許咱們請求和接收HttpClient實例。
咱們使用HttpClientFactory建立客戶端。在幕後,HttpClientFactory將爲咱們建立一個新的HttpClient。可是等等,以前說過爲每一個請求使用新的HttpClient是很糟糕。但此處的建立的httpclient是在他所管理的池子中,並不每一個請求都會是新的socket。
HttpClientFactory收集這些HttpClientHandler實例並管理它們的生命週期,以解決以前提到的一些問題。每次咱們要求HttpClient時,咱們都會獲得一個新實例,它可能(或可能不)使用現有的HttpClientHandler。HttpClient自己並無問題。
一旦建立,由此建立的全部HttpClientHandler將被默認保持約2分鐘。這意味着針對同一個CreateClient的任何新請求均可以共享處理程序,所以也能夠共享鏈接。當HttpClient存在時,它的處理程序將保持可用狀態,而且它將再次共享鏈接。
兩分鐘後,每一個HttpClientHandler都標記爲已過時。過時狀態只是標記它們,以便在建立任何新的HttpClient實例時再也不使用它們。可是,它們不會當即銷燬,由於其餘HttpClient實例可能正在使用它們。HttpClientFactory使用後臺服務監視過時的處理程序,一旦它們再也不被引用,就能夠正確釋放它們,也容許它們的鏈接被關閉。
經過使用HttpClientfactory咱們不須要考慮如何管理HttpClient的生命週期或擔憂遇到DNS問題。以上只是HttpClient小小的最佳使用推薦,還有其餘高級用法,例如和Polly的結合使用。
參考:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore