C# HttpClient使用和注意事項,.NET Framework連線池併發限制

XSpringSun發表於2023-02-24

System.Net.Http.HttpClient 類用於傳送 HTTP 請求以及從 URI 所標識的資源接收 HTTP 響應。 HttpClient 例項是應用於該例項執行的所有請求的設定集合,每個例項使用自身的連線池,該池將其請求與其他請求隔離開來。 從 .NET Core 2.1 開始,SocketsHttpHandler 類提供實現,使行為在所有平臺上保持一致。

HttpClient例項是執行網路請求的設定集合,每個例項會使用一個連線池。透過這段描述我們知道實際使用HttpClient的時候我們只需要例項化一個就行了,在處理程式例項內池連線,並在多個請求之間重複使用連線。也就是官方提倡的使用單個例項,如果每次請求就例項化一個HttpClient,則會建立不必要的連線降低效能,並且TCP 埠不會在連線關閉後立即釋放。

所以如果是大批次建立HttpClient請求則大量負載下可用的套接字數將耗盡,這種耗盡將導致 SocketException 錯誤。

使用方式

  1. 使用靜態變數。
static readonly HttpClient httpClient = new HttpClient();

  1. 使用單例模式
    public class HttpClientInstance
    {
        private static readonly HttpClient _HttpClient;

        static HttpClientInstance()
        {
            _HttpClient = new HttpClient();
        }

        public static HttpClient GetHttpClient()
        {
            return _HttpClient;
        }
    }

例項化引數

可以透過構造引數(如 HttpClientHandler (或 SocketsHttpHandler .NET Core 2.1 或更高版本) )作為建構函式的一部分來配置其他選項。 例項化HttpClient後無法更連線屬性,因此,如果需要更改連線屬性,則需要建立新的 HttpClient 例項。

配置可以在構造期間配置 HttpClientHandler 或 SocketsHttpHandler 傳入,SocketsHttpHandler可以設定額外引數包括 MaxConnectionsPerServer, PooledConnectionIdleTimeout、PooledConnectionLifetime、ConnectTimeout。

  • MaxConnectionsPerServer:HttpClient 物件所允許的最大併發連線數。
  • PooledConnectionIdleTimeout: PooledConnectionLifetime 指定的時間範圍過後,系統會關閉連線,然後建立一個新連線。
  • PooledConnectionLifetime:指定要用於連線池中每個連線的超時值。 如果連線處於空閒狀態,則連線會立即關閉;否則,連線在當前請求結束時關閉。
  • ConnectTimeout:指定在請求需要建立新的 TCP 連線時使用的超時。 如果發生超時,將取消請求 Task
var handler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};
var httpClient = new HttpClient(handler);

在.NET Framework 只能使用HttpClientHandler,且沒有PooledConnectionIdleTimeout和PooledConnectionLifetime等引數。

HttpClientHandler httpClientHandler = new HttpClientHandler();
//最大併發連線數
httpClientHandler.MaxConnectionsPerServer = 100;

HttpClient httpClient=new HttpClient(httpClientHandler);
//超時設定
httpClient.Timeout = new TimeSpan(5000);

可以是設定MaxConnectionsPerServer,可以設定Timeout。Timeout 為來自 HttpClient 例項的所有 HTTP 請求設定預設超時。 超時僅適用於導致啟動請求/響應的 xxxAsync 方法。 如果達到超時,則會 Task 取消該請求。這個超時時間是包含從請求到響應的整個時間段,而不像上面引數可以設定連線超時。

請求實現

HttpClient這是一個高階 API,用於包裝其執行的每個平臺上可用的較低階別功能。

在每個平臺上, HttpClient 嘗試使用最佳可用傳輸:
image

注意事項

在上面實現可以看到在不同的框架下HttpClient的實現是不一樣的,在.NET Framework下是使用HttpWebRequest支援。

所以還會受限HttpWebRequest的實現,如果我們要啟用多執行緒高頻率呼叫介面,那麼這裡要注意HttpWebRequest的連線併發的數量限制。HttpWebRequest透過ServicePoint設定,我們透過反編譯看到HttpWebRequest建構函式。
image

ServicePoint.DefaultConnectionLimit獲取允許的最大併發連線數。 對於 ASP.NET 託管的應用程式,預設連線限制為 10,對於所有其他應用程式,預設連線限制為 2。DefaultConnectionLimit 對現有 ServicePoint 物件沒有影響;它隻影響更改後初始化的物件。如果未直接或透過配置設定此屬性的值,則該值預設為常量 DefaultPersistentConnectionLimit

image

如果是應用連線池預設只有2個併發,所以當你啟用很多執行緒的時候實際效率是不會提升的,一直只有兩個併發在阻塞排隊,如果請求比較耗時後面的請求還有異常的可能。

因此當你使用多執行緒的時候要注意初始化HttpClient的httpClientHandler.MaxConnectionsPerServer = n;該引數用於設定。

RestSharp

平時我們可能使用RestSharp 用於網路請求,實際也是在HttpWebRequest上的封裝,在官網我們可以看到如下說明:
image

在最新的v107換成了HttpClient,以前的版本也是HttpWebRequest。如果要設定RestSharp的連線池併發數需要修改預設值。

System.Net.ServicePointManager.DefaultConnectionLimit = n;

然後再例項化RestClient。

相關文章