我曾经在 .NET 中写过一个 Crawler。为了提高它的可扩展性,我尝试利用 .NET 的异步 API。
System.Net.HttpWebRequest 具有异步 API BeginGetResponse/EndGetResponse。但是,这对 API 只是为了获取一个 HTTP 响应头和一个 Stream 实例,我们可以从中提取 HTTP 响应内容。所以,我的策略是使用 BeginGetResponse/EndGetResponse 异步获取响应 Stream,然后使用 BeginRead/EndRead 从响应 Stream 实例中异步获取字节。
在 Crawler 进行压力测试之前,一切似乎都很完美。在压力测试下,Crawler 内存使用率很高。我用 WinDbg+SoS 检查了内存,发现很多字节数组都被 System.Threading.OverlappedData 实例固定了。在互联网上搜索后,我从微软找到了这个 KB http://support.microsoft.com/kb/947862 。
根据 KB,异步 I/O 的数量应该有一个“上限”,但它并没有告诉一个“建议的”界限值。所以,在我看来,这个知识库没有任何帮助。这显然是一个 .NET 错误。最后,我不得不放弃从响应流中异步提取字节的想法,而只是以同步的方式进行。
.NET 库允许使用点网套接字(Socket.BeginSend / Socket.BeginReceive / NetworkStream.BeginRead / NetworkStream.BeginWrite)进行异步 IO,其异步 IO 的未完成缓冲区数量(发送或接收)必须具有上限.
网络应用程序应该对其发布的未完成异步 IO的数量有一个上限 。
编辑:添加一些问号。
有人有在 Socket 和 NetworkStream 上进行异步 I/O 的经验吗?一般来说,生产中的爬虫是同步还是异步与互联网进行I/O?