分別使用 HttpClient Factory / 靜態 HttpClient / HttpClient 執行個體 做多次遠端 同步或者非同步 Web API 存取之效能比較
在上一篇文章中 為什麼需要使用非同步程式設計,真的可以提升整體應用程式的執行效能嗎?,將針對要進行遠端 Web API 服務呼叫的時候,到底用戶端與伺服器端,若採用同步呼叫方式或者非同步呼叫的方式,會造成甚麼樣子的影響?在這篇文章中,做了一系列的分析與比較,並且對於遠端 Web API 部分,則是也有針對本地端的 Web API Server與在 Azure 上的 Web API Server 也分別作出測試,得到相關比較數據。
在上篇文中,在用戶端將會使用 new HttpClient() 表示式來產生出一個 HttpClient 物件,並透過此物件來進行遠端 Web API 的呼叫;不過,在 .NET 開發環境中,除了這個方式之外,還可以使用靜態 HttpClient 物件與 HttpClient 工廠 Factory 的方式來建立起或者取得一個 HttpClient 物件。
因此,在這篇文章中,將會分別使用這三種方式來取得或者建立起一個 HttpClient 物件,接著對遠端 Web API 伺服器服務呼叫 100 次,然而,在此也會分別使用同步與非同步方式進行 Web API 的需求存取;另外,在這裡也會分別建立起 .NET Framework 4.7.2 與 .NET Core 2.2 主控台應用程式 Console Application,使用相同的程式碼來進行測試,看看不同開發框架下會有何執行結果,而其中一個主要原因也是因為對於 HttpClient 工廠的用法,在 .NET Core 開發環境中已經已預設 NuGet 原生套件支援,在 .NET Framework 中,原生 BCL 內是沒有這樣的機制,另外,對於這兩個平台開發環境中的 執行緒集區 ,似乎這兩個平台的 CLR 對於 ThreadPool 的管理方式也有些差異,也可以透過篇文章中的測試程式,看看何者對於大量 Web API 存取上,何者表現
在這篇文章所提到的專案原始碼,可以從 GitHub 下載
使用 .NET Core 2.2 開發框架
當打開這個測試範例方案,將會看到有兩個專案,一個是使用 .NET Core 所建立的主控台應用程式,另外一個是使用 .NET Framework 所建立的主控台應用程式,兩者程式碼大致都相同,只不過因為在 .NET Framework 內並沒有提供 HttpClient 工廠,所以,對於使用 HttpClient 工廠進行測試的程式碼,並沒有存在於 .NET Framework 專案內。
在這個主控台應用程式專案,將是使用 .NET Core 2.2 開發框架所建立的,在這裡分別提供了九種測試方法,請參考底下的程式碼片段,每個測試方式都會進行遠端 Web API 存取 100 次的動作,當整體測試專案執行完畢之後,將會顯示出這次的執行過程將會耗費掉多少的電腦時間。
底下的表格將會是在這台具有 8 顆邏輯處理器的電腦上執行出來的結果。這裡分別會使用三種方式來取得或者建立 HttpClient 物件。
對於在用戶端使用同步方式來呼叫遠端同步 Web API 的方式,不會使何種方式取得繪者建立起 HttpClient 物件,所得到的表現結果都是最差勁的,大約需要耗費掉 120 秒以上的時間,才能夠完成 100 次的連續 Web API 呼叫。而最好的執行效能表現將會是用戶端與伺服器端的服務端點都是使用非同步的方式來設計,這樣要連續存取 100 次相同的 Web API,大約僅需要 4.5 秒左右的時間。
對於所測試的遠端 Web API 測試端點,將會模擬暫停 1.2秒的時間才會結束該服務的呼叫,因此,理論上每次呼叫一個 Web API ,都至少需要耗費掉 1.2 秒的時間。
在整體表現上,對於使用 new HttpClient() 表示式來建立起一個新的 HttpClient 執行個體所得到的執行效果似乎比較好一點點,個人覺得差異不太大。
New HttpClient | Static HttpClient | HttpClient Factory | |
---|---|---|---|
同步呼叫遠端同步 API | 138,677ms | 127,449ms | 131,588ms |
非同步呼叫遠端同步 API | 16,318ms | 26,466ms | 28,675ms |
非同步呼叫遠端非同步 API | 4,504ms | 4,753ms | 4,822ms |
Stopwatch sw = new Stopwatch();
sw.Start();
#region HttpClient Factory
// 用戶端使用 HttpCliet 工廠且同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientFactorySyncConnectSyncWebAPIAsync();
// 用戶端使用 HttpCliet 工廠且非同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientFactoryAsyncConnectSyncWebAPIAsync().Wait();
// 用戶端使用 HttpCliet 工廠且非同步等待結果,呼叫遠端非同步 Web API
//UsingHttpClientFactoryAsyncConnectAsyncWebAPIAsync().Wait();
#endregion
#region HttpClient Static Singleton
// 用戶端使用 HttpCliet Static Singleton且同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientStaticSingletonSyncConnectSyncWebAPIAsync();
// 用戶端使用 HttpCliet Static Singleton且非同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientStaticSingletonAsyncConnectSyncWebAPIAsync().Wait();
// 用戶端使用 HttpCliet Static Singleton且非同步等待結果,呼叫遠端非同步 Web API
//UsingHttpClientStaticSingletonAsyncConnectAsyncWebAPIAsync().Wait();
#endregion
#region New HttpClient
// 用戶端使用 New HttpCliet 且同步等待結果,呼叫遠端同步 Web API
//UsingNewHttpClientSyncConnectSyncWebAPIAsync();
// 用戶端使用 New HttpCliet 且非同步等待結果,呼叫遠端同步 Web API
//UsingNewHttpClientAsyncConnectSyncWebAPIAsync().Wait();
// 用戶端使用 New HttpCliet 且非同步等待結果,呼叫遠端非同步 Web API
// UsingNewHttpClientAsyncConnectAsyncWebAPIAsync().Wait();
#endregion
sw.Stop();
Console.WriteLine($"花費時間: {sw.ElapsedMilliseconds} ms");
使用 .NET Framework 4.7.2 開發框架
在 .NET Framework 4.7.2 這個主控台應用程式專案內,也在這裡分別提供了6種測試方法,請參考底下的程式碼片段,每個測試方式都會進行遠端 Web API 存取 100 次的動作,當整體測試專案執行完畢之後,將會顯示出這次的執行過程將會耗費掉多少的電腦時間。
底下的表格將會是在這台具有 8 顆邏輯處理器的電腦上執行出來的結果。這裡分別會使用三種方式來取得或者建立 HttpClient 物件。
對於在用戶端使用同步方式來呼叫遠端同步 Web API 的方式,不會使何種方式取得繪者建立起 HttpClient 物件,所得到的表現結果都是最差勁的,大約需要耗費掉 120 秒以上的時間,才能夠完成 100 次的連續 Web API 呼叫。而最好的執行效能表現將會是用戶端與伺服器端的服務端點都是使用非同步的方式來設計,這樣要連續存取 100 次相同的 Web API,大約僅需要 1.7 秒左右的時間。
對於所測試的遠端 Web API 測試端點,將會模擬暫停 1.2秒的時間才會結束該服務的呼叫,因此,理論上每次呼叫一個 Web API ,都至少需要耗費掉 1.2 秒的時間。
在整體表現上,對於使用 new HttpClient() 表示式來建立起一個新的 HttpClient 執行個體與用戶端和伺服器端都使用非同步方式所設計出來的程式碼,所得到的執行效果是最好的。
New HttpClient | Static HttpClient | HttpClient Factory | |
---|---|---|---|
同步呼叫遠端同步 API | 135,159ms | 125,293ms | X |
非同步呼叫遠端同步 API | 8,774ms | 61,519ms | X |
非同步呼叫遠端非同步 API | 1,718ms | 61,095ms | X |
Stopwatch sw = new Stopwatch();
sw.Start();
#region HttpClient Static Singleton
// 用戶端使用 HttpCliet Static Singleton且同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientStaticSingletonSyncConnectSyncWebAPIAsync();
// 用戶端使用 HttpCliet Static Singleton且非同步等待結果,呼叫遠端同步 Web API
//UsingHttpClientStaticSingletonAsyncConnectSyncWebAPIAsync().Wait();
// 用戶端使用 HttpCliet Static Singleton且非同步等待結果,呼叫遠端非同步 Web API
//UsingHttpClientStaticSingletonAsyncConnectAsyncWebAPIAsync().Wait();
#endregion
#region New HttpClient
// 用戶端使用 New HttpCliet 且同步等待結果,呼叫遠端同步 Web API
//UsingNewHttpClientSyncConnectSyncWebAPIAsync();
// 用戶端使用 New HttpCliet 且非同步等待結果,呼叫遠端同步 Web API
//UsingNewHttpClientAsyncConnectSyncWebAPIAsync().Wait();
// 用戶端使用 New HttpCliet 且非同步等待結果,呼叫遠端非同步 Web API
UsingNewHttpClientAsyncConnectAsyncWebAPIAsync().Wait();
#endregion
sw.Stop();
Console.WriteLine($"花費時間: {sw.ElapsedMilliseconds} ms");