1. 程式人生 > >ASP.Net Core2.1中的HttpClientFactory系列一

ASP.Net Core2.1中的HttpClientFactory系列一

引言:

  ASP.NET Core2.1 中出現了一個新的 HttpClientFactory 功能, 它有助於解決開發人員在使用 HttpClient 例項從其應用程式中訪問外部 web 資源時可能遇到的一些常見問題。關於HttpClientFactory 到底解決了那些HttpClient的嚴重問題,下面是我羅列出來的(原文來自於:https://www.infoq.com/news/2016/09/HttpClient)

  (1)在處理HttpClient物件的時候不會立即關閉socket。

  (2)太多的例項影響效能

  (3)單例的HttpClient或者共享HttpClient例項,不遵守DNS 生存時間 (TTL) 設定。(這個問題我也不太明白,具體怎麼重現這個問題,我下去再研究研究。)

HttpClientFactory這個小可愛,解決了上面的所有問題,他也是ASP.NET Core2.1最新特點之一,下面詳細聊聊HttpClient存在的這些問題。

一、HttpClient存在的問題

  由於設計錯誤、bug 和文件不正確等因素, 導致在.Net中正確使用HttpClient 出奇的難。因此, 在生產環境中看起來正常工作的應用程式可能會在負載大的情況下產生效能和執行時故障的問題。

  為了理解我們為什麼遇到這種情況, 我們首先要看另一個面向連線的類: SqlConnection。 這個類實現了IDisposable介面,所以絕大多數開發人員都是這樣寫的,例項如下:

  using (var con = new SqlConnection(connectionString)) {    

con.open();    //use the connection here  } //this closes the connection  

雖然,這個例子在解釋HttpClient存在的問題不是很到位,但是使用這種方式,來寫上面的程式碼,是沒錯的。如果你嘗試這把這種模式應用到實現了IDisposable介面的HttpClient,則會遇到一些很奇怪的問題。具體的說,它會開啟比實際需要更多的socket,加重了伺服器的負載。此外使用using語句是不會關閉這些套接字的,相反,在應用程式停止使用它們時,會關閉幾分鐘。

Connection Pooling

回到 SqlConnection 示例中, 大多數面向連線的資源都是有連線池的。當您 "開啟 " 資料庫連線時, 它首先檢查池中是否有可用的、未使用的連線。如果它找到一個, 將重用它, 而不是建立一個新的連線。

同樣, 當您 "關閉 " SqlConnection 它只是將連線釋放到連結池中。最終, 一個單獨的程序可能會關閉長時間未使用的連線。

HttpClient 不這樣做。當您處理它時, 它將啟動關閉它所控制的套接字的過程。這意味著下次有請求時, 您必須經過一個全新的連線週期。如果您的網路有很高的延遲或您的連線是安全的, 則這會特別痛苦, 因為後者需要新一輪的 SSL/TLS 協商。

Closing a Socket Takes Four Minutes

如上所述,關閉套接字不是一個快速的過程。 當你“關閉”套接字時,你真正在做的是將它置於TIME_WAIT狀態。 Windows將在此狀態下保持連線240秒,以防萬一剩餘的資料包仍在傳輸中。

這使您更有可能耗盡可用套接字的數量,從而導致執行時錯誤,例如“無法連線到遠端伺服器.System.Net.Sockets.SocketException:每個套接字地址只有一種用法(協議/ 網路地址/埠通常是允許的“。

下面我們來實踐一下,看看真相:

示例演示:(注意使用的是.Net Core 1.0)

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Starting connections");
            for (int i = 0; i < 10; i++)
            {
                using (var client = new HttpClient())
                {
                    var result = await client.GetAsync("http://aspnetmonsters.com/");            Console.WriteLine(result.StatusCode);           }        }       Console.WriteLine("Connections done");       Console.ReadKey();     }   } }

 這將會開啟10個請求,並以get的方式,去請求部落格園,我們只打印出狀態碼。

輸出結果:

到這個地步,可能我們就會很高興,搞定!閃人!,真的搞定了嗎?我的小可愛,下面我們使用netstat 工具並檢視執行它的機器上的套接字狀態,我們將看到:

看到沒,我的應用程式已經執行完了,但是,仍然有很多連結在開啟上面圖示中的主機,它們都處於TIME_WAIT狀態,這意味這我們在應用程式這邊已經把連結關閉了,但是我們仍然在等待檢視,是否有額外的資料包進來,使用這些連結。因為它們可能在網路的某個地方已經被延遲,下面我們來看一張TCT/IP圖,該圖引自(https://www4.cs.fau.de/Projects/JX/Projects/TCP/tcpstate.html)

Windows將在此狀態下保持連線240秒(由[HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Tcpip \ Parameters \ TcpTimedWaitDelay]設定)。 Windows可以快速開啟新套接字的速度有限,因此如果您耗盡連線池,那麼您可能會看到如下錯誤:

Unable to connect to the remote serverSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.

在谷歌搜尋會給你一些關於減少連線超時的可怕建議。 事實上,當在伺服器上執行著正確使用HttpClient或類似構造的應用程式時,減少超時可能會導致其他不利後果。 我們需要了解“正確”意味著什麼,並去修復底層的問題而不是去修補機器級的變數。

如果我們共享一個HttpClient例項,那麼我們可以通過重用它們來減少套接字的浪費:

請注意,我們只為整個應用程式共享了一個HttpClient例項。 仍然可以正常工作(實際上由於套接字重用而快一點)。 Netstat現在只顯示:

 

好了,總結一下:在.Net Core 1.0之前的版本,使用的時候需要注意下面的兩點:

(1)確保你的HttpClient 是 static

(2)不要丟棄或包裝HttpClient 在一個using塊中。

小弟我才疏學淺,有不對的地方可以指出來,共同探討。其實,我們一直使用using把它包起來,也是沒錯的,是因為HttpClient實現了IDisposable ,但是HttpClient就比較特殊,這不怪我們,文件就是錯誤的。

二、HttpClientFactory in ASP.NET Core 2.1就解決了上面所有的問題

HttpClientFactory 這個小可愛,就解決了上面的所有問題,她也是ASP.NET Core 2.1中最新特點之一,有了她我們就不用關心如何建立HttpClient,又如何釋放它。關於如何使用它,部落格園中的有介紹的,我這裡就不再講述了。

具體請參考:https://www.cnblogs.com/willick/p/9640589.html

三、總結

到這裡該系列文章的第一篇就講完了,有不對的地方,還請大佬們能指出來,共同探討,好了,希望對你有所幫助,該系列文章,每週末更新,愛看不看,哈哈哈~~~~~~

參考文章:

(翻譯)https://www.infoq.com/news/2016/09/HttpClient

(翻譯)https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

作者:郭崢

出處:http://www.cnblogs.com/runningsmallguo/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。