1. 程式人生 > >.NetCore 2.1中的HttpClientFactory最佳實踐

.NetCore 2.1中的HttpClientFactory最佳實踐

response ttpClient 團隊開發 分享圖片 如果 encode don 包含 rdquo

原文:.NetCore 2.1中的HttpClientFactory最佳實踐

.NET Core 2.1中的HttpClientFactory最佳實踐

ASP.NET Core 2.1中出現一個新的HttpClientFactory功能,

它有助於解決開發人員在使用HttpClient實例從其應用程序發出外部Web請求時可能遇到的一些常見問題。

介紹

在.NETCore平臺的2.1新增了HttpClientFactory,雖然HttpClient這個類實現了disposable,但使用它的時候用聲明using包裝塊的方式通常不是最好的選擇。處理HttpClient,底層socket套接字不會立即釋放。該HttpClient類

是專為多個請求重復使用而創建的。需要不同的基地址,不同的HTTP標頭和其他對請求個性化操作的場景時,需要手動管理多個HttpClient實例,為了簡化HttpClient實例管理,.NET Core 2.1提供了一個新的HTTPClientFactory - 它可以創建,緩存和處理HttpClient實例。

什麽是HttpClientFactory?

用ASP.NET團隊的話說:“an opinionated factory for creating HttpClient instances”(一個用於創建HttpClient實例的最佳實踐的工廠),並且是ASP.NET Core 2.1發布的新功能。

根據大家以前使用HttpClient的經驗,您可能遇到一些困擾的問題,有時甚至沒有意識到您有問題(只是在並發並不大的場景沒觸發而已)。

第一個問題是當你在代碼中創建太多的HttpClients時,這反過來會產生兩個問題......

  1. 這是低效的,因為每個請求都有自己的遠程服務器連接池。這意味著您需要為每個創建的客戶端支付重新連接到該遠程服務器的成本。
  2. 更大的問題是如果你創建了很多HttpClient並使用到他們,你可以遇到Socket耗盡,而你基本上已經太快地使用了過多的Socket。您可以同時打開多個Socket是有限制的。當您dispose銷毀HttpClient時,它打開的連接在TIME_WAIT狀態下保持打開狀態最長240秒(如果來自遠程服務器的任何數據包仍然通過)。

HttpClient實現了IDisposable,這通常會導致開發人員在使用IDisposable對象時遵循正常模式,在using塊中創建它。這樣可以確保一旦完成對象並且它已經超出範圍,就可以正確銷毀對象。

因此,最優的方法是重用HttpClient實例,以便也可以重用連接。HttpClient是一個可變對象,但只要你沒有運行期改變它,它實際上是線程安全的並且可以共享。因此,一種常見的方法是將其註冊為具有DI框架的單例模式,或者創建包含static靜態實例的對象。

但是,這會產生新問題。以這種方式使用單個HttpClient將保持連接打開並且不遵守DNS生存時間(TTL)設置(總之就是同一個HttpClient實例只能有一個請求頭,在被請求方發生更改時,由於是單例不能做個性化改變,否則導致其他請求失敗)。現在連接將永遠不會獲得DNS更新,因此您正在與之通信的服務器將永遠不會更新其地址。在某些情況下,這是完全有可能的,在以上這種情況下,您可以平衡許多主機,這些主機可能隨著時間的推移而改變,或者可能使用Blue/Green 部署推出新服務。如果服務器消改變,則您的連接使用的IP可能不再響應您通過單個HttpClient發出的請求。

所以需要我們手動去管理每類服務器的HttpClient的實例來進行個性化請求頭的構造和發起請求!

HttpClientFactory旨在幫助您開始解決這些問題,並提供了一種新的機制來創建在幕後為我們正確管理的HttpClient實例。它將為我們“做管理HttpClient的事”,我們可以專註於業務!雖然在參考HttpClient時提到了上述問題,但事實上問題的根源實際上發生在HttpClient上,HttpClient使用了HttpClientHandler。HttpClientFactory管理處理程序的生命周期,以便我們有一個可以重用的池,同時還可以(Rotating)輪換它們以使DNS不會過時。

使用HttpClient的昂貴部分實際上是創建HttpClientHandler和連接。以這種HttpClientFacotry方式匯集這些內容意味著我們可以更高效利用資源最節省地使用我們系統上的socket。當您使用HttpClientFactory請求HttpClient時,實際上每次都會獲得一個新實例,這意味著我們不必擔心會改變它的狀態。此HttpClient可能(或可能不)使用池中的現有HttpClientHandler,從而使用現有打開的連接。

默認情況下,每個新創建的HttpClientHandler(派生自HttpMessageHandler)生命周期只有2分鐘。通過services.AddHttpClient()創建HttpClientFactory實例時,可以根據每一個命名的Client客戶機進行控制。達到生命周期後,處理程序將不會立即被釋放掉,而是放入過期的池中。任何依賴於HttpClientFactory的處理程序鏈的客戶端都可以繼續使用它而沒有任何問題。有一個後臺作業檢查過期的池,以查看處理程序的所有引用是否已在scope之外,此時可以將其釋放掉。處理程序鏈過期後對新客戶端的任何新請求都將獲得新的處理程序鏈。

這種方法運行得相當不錯,但.NET Core方面還有其他一些事情可能會進一步改善這種情況。.NET Core團隊開發了一個新的ManagedHandler,它可以更正確地管理DNS,原則上可以保持更長時間,這意味著可以更有效地共享連接。這個新的處理程序還被設計為在不同的操作系統中更加一致地運行。在該工作完成之前,上面的處理程序池是一個合理的解決方法。

如何使用HttpClientFactory

我們將首先創建一個簡單的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

.NetCore 2.1中的HttpClientFactory最佳實踐