当前位置: 首页 > 编程语言 > C#

C#学习教程:.NET没有可靠的异步套接字通信?分享

时间:2023-04-11 03:19:54 C#

.NET没有可靠的异步套接字通信吗?我曾经在.NET中写过一个Crawler。为了提高其可伸缩性,我正在尝试利用.NET的异步API。System.Net.HttpWebRequest具有异步APIBeginGetResponse/EndGetResponse。然而,这对API只是为了获取HTTP响应标头和Stream实例,我们可以从中提取HTTP响应内容。因此,我的策略是使用BeginGetResponse/EndGetResponse异步获取响应流,然后使用BeginRead/EndRead从响应流实例中异步获取字节。在对爬虫进行压力测试之前,一切看起来都很完美。在压力测试下,爬虫内存占用过高。我用WinDbg+SoS检查了内存,它说System.Threading.OverlappedData实例有很多字节数组。经过一些互联网搜索后,我从Microsoft找到了这个KBhttp://support.microsoft.com/kb/947862。根据KB,异步I/O的数量应该有一个“上限”,但它没有告诉“推荐的”绑定值。因此,在我看来,此知识库文章没有任何帮助。这显然是一个.NET错误。最后只好放弃从响应Stream中异步提取字节的想法,干脆同步进行。允许带点网络套接字的异步IO的.NET库(Socket.BeginSend/Socket.BeginReceive/NetworkStream.BeginRead/NetworkStream.BeginWrite)必须为其异步IO的未完成缓冲区(发送或接收)数量设置上限。Web应用程序应该对其发出的未完成异步IO数量有一个上限。编辑:添加了一些问号。有人在Socket和NetworkStream上使用过异步I/O吗?一般而言,生产中的爬虫是同步还是异步地通过Internet执行I/O?Hmya,这不是.NET框架问题。链接的知识库文章可能会更明确一些:“你正在使用一把上膛的枪,当你用脚瞄准时会发生这种情况”。该枪中的子弹是.NET,它使您能够发起尽可能多的异步I/O请求。它会做你要求它做的事情,直到你达到某种资源限制。在这种情况下,gen0堆中可能有太多的固定接收缓冲区。资源管理仍然是我们的工作,而不是.NET。这与没有绑定的内存分配没有什么不同。解决这个特殊问题需要限制未完成的BeginGetResponse()请求的数量。有数百个没有意义,每个都必须一次挤过Intertube。添加另一个请求只会导致它需要更长的时间才能完成。或者让你的程序崩溃。你显然想限制并发请求的数量,无论你的爬虫是同步的还是异步的。这个限制不是固定的,它取决于你的硬件、网络……我不太确定你的问题是什么,因为HTTP/Sockets的.NET实现是“好的”。有一些错误(请参阅我关于正确控制超时的帖子),但它完成了工作(我们有一个每秒获取数百页的生产爬虫)。顺便说一句,我们使用同步IO,只是为了方便。每个任务都有一个线程,我们限制并发线程数。对于线程管理,我们使用MicrosoftCCR。这不仅限于.Net。这是一个简单的事实,即每个异步请求(文件、网络等)都使用内存和(至少在某些时候是网络请求)非分页池(有关您在非托管代码中可能遇到的问题的更多信息,请参见此处)。因此,未完成请求的数量受内存量的限制。在Vista之前,有一些非常低的非分页池限制会在内存用完之前导致问题,但在post-vista环境中,非分页池的使用要好得多(请参阅此处)。它在托管代码中有点复杂,因为除了非托管世界中的问题之外,您还必须处理异步请求的内存缓冲区在这些请求完成之前被固定的事实。听起来你在读取时遇到了这些问题,但在写入时情况更糟,如果不是更糟的话(一旦TCP流控制启动连接,然后发送开始需要更长的时间才能完成,因此缓冲区固定时间越来越长-参见此处和这里)。问题不在于.Net异步的东西坏了,而在于抽象使得它看起来比实际更容易。例如,为了避免固定问题,在程序启动时将所有缓冲区分配到一个大的连续块中,而不是按需分配......就个人而言,我会用非托管代码编写这样的爬虫,但那只是我;)你会仍然有很多问题,但是您将对它们有更多的控制权。没有任何知识库文章可以给你一个上限。上限可能因可用硬件而异——对于具有16gram的机器,对于2Gram机器,上限会有所不同。它还取决于GC堆的大小、片段的碎片化程度等。您应该做的是对后面的指标使用信封计算。算出每分钟要下载的页数。这应该确定您想要多少异步请求(N)。知道N后,创建一段代码(如生产者-消费者管道的消费者端)可以创建N个未完成的异步下载请求。一旦请求完成(由于超时或由于成功),另一个异步请求将通过从队列中拉出工作项来启动。您还需要确保队列不会越界,例如如果由于某种原因下载速度变慢。当您使用套接字的异步Send(BeginSend)方法时会发生这种情况。如果使用自己自定义的线程池,并通过线程同步send方式发送数据,主要是为了解决这个问题。经过测试和认证。以上是C#学习教程:.NET没有可靠的异步套接字通信?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: