HttpClient
使用HttpClient可以很方便的請求Web API,但在使用時有一些需要注意的地方,不然會給你的程式帶來毀滅性的問題。
HttpClient是一個繼承了IDisposable
介面的物件,所以在使用的時候,需要主動呼叫Dispose
方法來釋放它。或者使用using
:
using(var client = new HttpClient()) { //do something with http client }
這看起來似乎沒什麼問題。我們使用一些程式碼來測試一下它,我們將發起10個GET請求:
namespace ConsoleApplication { public class Program { public 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://www.zkea.net"); Console.WriteLine(result.StatusCode); } } Console.WriteLine("Connections done"); } } }
輸出結果如下:
Starting connections OK OK OK OK OK OK OK OK OK OK Connections done
看起來一切正常,但實際上並不是!我們使用netstat
來檢視一下sockets的使用情況:
C:Userswayne>NETSTAT.EXE ... Proto Local Address Foreign Address State TCP 10.211.55.6:12050 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12051 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12053 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12054 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12055 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12056 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12057 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12058 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12059 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12060 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12061 47.74.132.243:http TIME_WAIT TCP 10.211.55.6:12062 47.74.132.243:http TIME_WAIT ...
雖然應用程式已經退出,但是剛才發起的連線仍處於 TIME_WAIT
狀態。TIME_WAIT
狀態是指連線已經在一邊關閉,但仍在等待是否有其他資料包出現, 因為它們可能在網路上的某個地方被延遲,socket資源並沒有立即被回收。所以,如果你的程式(網站)的併發量很大,而每一次都例項化一個HttpClient物件,你的程式將會消耗掉伺服器上所有可用的socket資源,並導致程式出現異常,不可正常訪問。
正確使用HttpClient
HttpClient裡面的方法都是執行緒安全的:
CancelPendingRequests DeleteAsync GetAsync GetByteArrayAsync GetStreamAsync GetStringAsync PostAsync PutAsync SendAsync
所以你應當只例項化一個HttpClient物件,並且不需要去主動釋放它,它會在你程式退出的時候一起被釋放掉。
我們對程式做以下修改再測試一下:
namespace ConsoleApplication { public class Program { private static HttpClient Client = new HttpClient(); public static async Task Main(string[] args) { Console.WriteLine("Starting connections"); for(int i = 0; i<10; i++) { var result = await Client.GetAsync("http://aspnetmonsters.com"); Console.WriteLine(result.StatusCode); } Console.WriteLine("Connections done"); Console.ReadLine(); } } }
再看看socket使用情況,這下就一切正常了:
TCP 10.211.55.6:12254 47.74.132.243:http ESTABLISHED