HttpClient是否有默認並發數限制?
在.Net 4.0之前,一直是依靠HttpWebRequest實現Http操作的。它默認有一個非常保守的同一站點下最大2並發數限制,導致默認情況下HttpWebRequest往往得不到理想的速度(估計這個策略郁悶了不少碼農),必須修改App.config或ServicePointManager.DefaultConnectionLimit的值。
MS在.Net 4.5中引入了一個HttpClient類專門處理Http操作,本來我以為HttpClient和HttpWebRequest遵循一樣的策略的。今天在寫一個多線程下載的程序的時候,用到了10個並發連接,發現在默認的情況下,HttpClient並沒有並發數限制。
最初我以為是.Net 4.5取消了這個並發數限制(畢竟現在基本上沒有誰遵循這個標准了),然后用WebRequest重寫了相關代碼,發現依然是2並發上限,並且ServicePointManager.DefaultConnectionLimit的值也是2。也就是說: HttpClient不受HttpWebRequest並發策略控制,也沒有系統級的並發限制。
另外,測試的時候發現,HttpWebRequest默認也不是對所有地址都2並發上限的。例如,對本地的http地址連接(http://localhost/*)就沒有並發限制。
C#多線程環境下調用 HttpWebRequest 並發連接限制
.net 的 HttpWebRequest 或者 WebClient 在多線程情況下存在並發連接限制,這個限制在桌面操作系統如 windows xp , windows 7 下默認是2,在服務器操作系統上默認為10. 如果不修改這個並發連接限制,那么客戶端同時可以建立的 http 連接數就只有2個或10個。對於一些諸如瀏覽器或網絡蜘蛛的應用,2個或10個並發數量實在太少,大大影響應用的性能。之所以有這個並發連接限制,是因為 http 1.0 和 http 1.1 標准規定並發連接數最大為2. 不過目前主流的瀏覽器都已經不遵循這個規則了,但 .net framework 依然默認遵循這個規則。
很多文章說用異步方式訪問 HttpWebRequest 可以提高並發性能,但我測試下來,如果不修改這個默認並發連接數,同步或異步方式訪問性能都很不好。
調整這個默認並發連接限制的方法很簡單
只要在程序中設置:
System.Net.ServicePointManager.DefaultConnectionLimit = 512;
這個值最好不要超過1024。
我們也可以在app.config 中對最大並發連接數進行設置,方法如下:
<configuration>
<system.net>
<connectionManagement>
<add address = "http://www.google.com" maxconnection = "512" />
<add address = "*" maxconnection = "512" />
</connectionManagement>
</system.net>
</configuration>
修改了這個設置后,並發性能明顯提高,從原來每秒鍾20次直接上升到每秒鍾1000多次。
注意DotNet的ConnectionLimit
由於不熟悉C#的開發,在做一個系統WS接口的壓力測試時走了彎路。發現這個問題的原委是要在用C#壓力測試我們的一個REST Web Service.服務器上我理論預計的性能是100並發,4s內響應完成。這個系統提供了給DotNet的客戶端,使用hammock庫編寫而成,在壓力測試中,系統性能總是上不來,在查看服務器日志后發現請求都是串行處理的,所以維持了400ms每個請求的性能水平,而理論上的十個通道的並行處理的性能沒有達到。我開始以為是否是同事編寫的客戶端有問題,檢查之后發現沒有任何問題;轉而尋求是否是Hammock庫的問題,未果。然后懷疑服務器的問題,我使用Java,restclient庫編寫了一個測試程序,發現10個通道全部啟用,達到了理論性能,服務器方面完成不成問題。所以應該是C#方面的問題,Hammock的源碼比較復雜,看了半天也沒啥發現,在我們的這個RestClient中有點殺雞用牛刀的感覺,是否是其中的什么Bug呢?所以准備自行編寫客戶端。由於服務器上使用Rest,XML傳輸格式的XSD文件也已經生成,客戶端上都是使用xsd文件反向生成的POCO對象,查了一下C#的文檔,直接使用XmlSerializer就可以很方便的實現序列化和反序列化了。並且System.Net命名空間中有HttpWebRequest類,很容易自行實現自己的客戶端,而不是用hammock庫,這樣就可以排除hammock的問題。昨晚自己寫了一下,一測試性能還是無法達到,使用netstat查看連接,居然發現同時還只有兩個連接到服務器。覺得奇怪,所以使用C# Socket Connection limit之類的關鍵字Google,原來C#類庫中,HttpWebRequest默認的最大連接數為2,為什么是個二呢?想不通,其實我覺得既然作為類庫,C#完全不必要限制客戶端的連接數,這是由程序員控制的啊。有兩個辦法設置不同的連接數。
1. HttpWebRequest.ServicePoint.ConnectionLimit
2. ServicePointManager.DefaultConnectionLimit
任意設置一個到我的最大並行處理數,比如時,性能馬上就上去了,幾乎逼近於理論性能,但是相比使用Java測試的結果還是要整體慢3s左右,因為不知道什么原因,在首次連接服務器時,會有一個幾秒的延遲。不知道具體原因是什么,是否又有什么默認設置?又經過了一番搜索和研究,終於發現了真正的原因,在使用HttpWebRequest類的時候,默認會去檢查代理服務器設置,這樣當然就慢了。而且可以在app.config中設置連接數和代理服務器的設置,而不需要在程序中硬編碼了。
<system.net>
<defaultProxy enabled="false">
<proxy/>
<bypasslist/>
<module/>
</defaultProxy>
<connectionManagement>
<add address="*" maxconnection="10"/>
</connectionManagement>
</system.net>
PS:最近用Visual Studio,用C#,不知道仍然是先入為主的習慣問題,總覺得沒有使用Eclipse編寫Java好用,首先是自己對代碼編寫的快捷鍵不熟悉,所以效率要慢一半
為什么不能用源碼綁定到dll上,就像在eclipse里把src綁定到jar包上一樣
為什么就沒有一個快捷鍵全部快速自動導入命名空間呢
為什么就沒有一個Ctrl+O,快速定位到類或者資源呢,不要跟我說Ctrl+,,把方法,類,字段啥都混在一起了
最后經過了一番搜索,發現了有個Visual Studio的插件,Productive PowerTools,可以增強有些特性,比如使用Alt+Up上移一行代碼,Alt+Down下移當前行代碼,這跟在Eclipse中的操作習慣一致了,不過這並沒有自動把移動的代碼格式化,這是Eclipse要強大的地方。