2008-10-25 54 views
7

我曾經在.NET中寫過一個Crawler。爲了提高可伸縮性,我試圖利用.NET的異步API。.NET沒有可靠的異步套接字通信?

System.Net.HttpWebRequest具有異步API BeginGetResponse/EndGetResponse。但是,這對API只是獲取HTTP響應頭文件和Stream實例,我們可以從中提取HTTP響應內容。所以,我的策略是使用BeginGetResponse/EndGetResponse異步獲取響應Stream,然後使用BeginRead/EndRead從響應Stream實例異步獲取字節。

一切似乎都是完美的,直到履帶車進行壓力測試。在壓力測試中,Crawler遭受高內存使用。我用WinDbg + SoS檢查了內存,並發現大量的字節數組是由System.Threading.OverlappedData實例引起的。在互聯網搜索後,我發現這個KB http://support.microsoft.com/kb/947862從微軟。

根據知識庫,異步I/O的數量應該有一個「上限」,但它不會告訴「建議」的限制值。所以,在我看來,這個KB無濟於事。這顯然是一個.NET錯誤。最後,我不得不放棄從響應Stream中進行異步提取字節的想法,只是以同步的方式進行。

在.NET庫,允許 異步IO用點網插座 (Socket.BeginSend/ Socket.BeginReceive/ NetworkStream.BeginRead/ NetworkStream.BeginWrite)必須對量的 上限緩衝區 未完成(發送或接收) 與他們的異步IO。

網絡應用應當具有 上限 優秀異步IO,它的帖子的數量。

編輯:添加一些問號。

任何人有任何經驗在Socket & NetworkStream上做異步I/O? 一般來說,生產中的爬蟲是否使用帶有同步或異步的互聯網進行I/O?

+0

不是一個單獨的問號,除了在主題...一個壞的標誌。 – 2008-10-25 10:02:19

回答

3

顯然你想限制併發請求的數量,不管你的爬蟲是同步/異步。這個限制是不固定的,這取決於你的硬件,網絡,...取決於你的硬件,網絡,...

我不太確定這裏有什麼問題,因爲HTTP /套接字的.NET實現是「OK」。有一些漏洞(關於正確控制超時,請參閱my post),但它可以完成工作(我們有一個每秒抓取數百頁的生產爬蟲)。

順便說一句,我們使用同步IO,只是爲了方便。每個任務都有一個線程,並且我們限制併發線程的數量。對於線程管理,我們使用了Microsoft CCR

+0

我毫不懷疑Socket上的同步I/O在DotNet中工作正常。我只是不相信它的異步I/O API。 – 2008-10-25 10:37:56

+0

問題是中止/取消操作,它在.NET中無法正常工作。 你應該總是喜歡同步API(超時),這樣你就不需要自己取消操作。 – ripper234 2008-10-30 15:05:30

10

Hmya,這不是一個.NET框架問題。鏈接的知識庫文章可能會更加明確:「您使用的是加載槍,這是當您將其瞄準在腳上時發生的情況。」槍中的子彈是.NET,讓你能夠啓動儘可能多的異步I/O請求。它會做你要求它做的事,直到你遇到某種資源限制。在這種情況下,可能在第0代堆中有太多的固定接收緩衝區。

資源管理仍然是我們的工作,而不是.NET的。這與分配無限制內存沒有區別。解決這個特定的問題需要您對未完成的BeginGetResponse()請求的數量進行限制。有數以百計的人沒有什麼意義,他們每個人都必須一次擠過Intertube。添加另一個請求只會導致它花費更長的時間才能完成。或者崩潰你的程序。

0

沒有KB文章可以給你一個上限。上限可以根據可用硬件而變化 - 對於裝有16g內存的機器,2G內存機器的上限是什麼?它也將取決於GC堆的大小,它是多麼零散等等。

你應該做的是用你自己的度量來使用信封計算。找出每分鐘要下載的頁數。這應該確定你想要的異步請求數(N)。一旦你知道N,就創建一段代碼(比如生產者 - 消費者管道的消費者端),它可以創建N個未完成的異步下載請求。一旦請求完成(無論是由於超時還是由於成功),通過從隊列中提取工作項來啓動另一個異步請求。

您還需要確保隊列不會超出邊界,例如,無論出於何種原因下載速度變慢。

0

當您使用套接字的異步發送(BeginSend)方法時發生這種情況。如果你使用你自己的自定義線程池,並通過線程發送數據同步Send方法主要是解決這個問題。經過測試和證明。

3

這不限於.Net。

這是一個簡單的事實,即每個異步請求(文件,網絡等)使用內存和(至少在某些時候用於聯網請求)非頁面緩衝池(請參閱here瞭解非託管代碼中可能遇到的問題的詳細信息)。未完成請求的數量因此受內存量的限制。在Vista之前,存在一些嚴重低的非頁面緩衝池限制,會在內存耗盡之前給您帶來問題,但在後Vista環境下,非頁面緩衝池使用會更好(請參閱here)。

這在託管代碼中稍微複雜一點,除了在非託管環境中遇到的問題之外,還必須處理這樣一個事實,即用於異步請求的內存緩衝區將被鎖定,直到這些請求完成。聽起來就像你在讀取時遇到了這些問題,但是對於寫入來說,如果不是更糟糕的話,它也同樣糟糕(只要TCP流量控制在連接上啓動,那些發送完成將開始花費更長的時間,因此這些緩衝區固定的時間越長越長 - 請參閱herehere)。

問題不在於.Net異步的東西壞了,只是抽象是這樣,它使得它看起來都比實際更容易。例如,爲了避免釘住問題,在程序啓動時將所有緩衝區分配到一個大的連續塊中,而不是根據需要進行分配......

個人而言,我會在非託管代碼中編寫這樣的爬蟲程序,但這是隻是我;)你仍然會遇到很多問題,但你對它們有更多的控制權。