Dns.BeginGetHost …方法阻塞

所以我想做很多 DNS查询。

我从Begin/EndGetHostEntry异步对创建(数千个)任务:

 var lookupTask = Task.Factory.FromAsync ( Dns.BeginGetHostEntry, (Func) Dns.EndGetHostEntry, "google.com", null ) 

然后Task.WaitAll完成所有事情。 我看到ThreadPool线程的数量急剧增加以响应我的请求。 如果我将ThreadPool minThreads为500,则工作量的消耗会大大加快。 所有这些都指向Dns异步实现中的阻塞。

如果我用管理的Dns客户端替换Dns ,我可以使用相同的工作负载,在ThreadPool只有1或2个线程,而cpu几乎是空闲的。

问题是, Dns实现绝对是许多网络API( HttpWebRequestWebClientHttpClient )的核心,它们似乎都受到这个问题的影响。 如果我使用第三方库解析DNS,并使用IP地址作为uri中的主机发出HTTP请求,然后更改Host标头以修复请求,与涉及System.Net.Dns任何内容相比,我都会获得极高的性能。

这里发生了什么? 我错过了什么或者System.Net.Dns实现真的那么糟糕吗?

System.Net.Dns使用windows gethostbyname函数进行DNS查询,并且根本没有异步函数。 BeginGetHostEntry函数基本上只是线程池上同步GetHostEntry调用的包装器。

上次我遇到慢速/同步DNS查找的同样问题时,我最终只使用了一个大型ThreadPool来完成工作,因为没有一个内置窗口或.net DNS相关function支持正确(并行)异步执行。

这可能不是一个完整的答案,但是:

在.net内解析的DNS打开与dns的连接,询问问题并关闭。 您链接的托管DNS客户端的示例清楚地显示,此库建立了连接,然后在保持打开状态时,您可以提出许多问题,就像这样做

 nslookup - >hostname1 >hostname2 ... 

在dos / unix下

通常在打开它时可能需要一段时间,通过多次调用已打开的连接,您不必对自己进行反向连接,并且本身,以及与dns服务器的连接在第一次连接时所做的所有其他垃圾。 例如:如果我列表中的第一个DNS服务器忙,我的计算机通常需要时间来解析可用的其他服务器,因此,如果您在.net下每次查看时都遇到过这种情况。库,您会看到漫长的等待,并且需要很multithreading,当然还会增加CPU负载,而实际上并不是很多。

实施不是“坏”,它不是为多批作业设计的。 除非有电话我也错过了。

我没有1000个URL的数据集来测试您的代码,并且重复请求相同的URL会导致命中缓存(而不是我的网络的DNS服务器)。 因此,请在测试后对成功/失败进行评论。

我对此(或任何其他假设)进行测试的建议是创建一个包含1000个URL的测试数据集,并对其进行编号。 然后设置一些日志记录(即:log4net或类似的),并在每个DNS解析任务完成时写出一条语句,包括已完成任务的索引。 我相信你会看到这1000个任务完全同步完成。 或者至少每组2-8个异步结果,其中2-8的所有组都是同步的。

原因是连接管理。 在内部.Net只允许与同一端点的这么多并发连接。 如果打开与dns服务器的1000连接,则一次只能创建几个连接。 其余的需要等到一些早期的连接关闭,然后才能建立到同一端点(您的DNS服务器)的另一个连接。

这种限制通常有充分的理由。 但是对于像DNS这样的数据量相对较少且服务请求成本相对较低的情况,我可以打开这个限制,最多可以说100-200个并发DNS请求。

您可以使用以下配置打开此限制:

        

System.Net.ConnectionManagement的MSDN

您可以指定特定的端点地址(URL或IP)以及该地址的最大连接数。 一些负载测试应用程序将只使用通配符*和65535来打开它的所有内容。

我怀疑托管DNS实现要么重用与DNS服务器相同的连接,要么具有如上所述的内部配置。

您可能在问题中包含的更多详细信息是您是在同一物理网络上查询本地DNS服务器还是从本地ISP或OpenDNS等公共DNS服务器查询DNS服务器。 这些特定DNS服务器的配置可能会有自己的限制(ISP可能会限速,我不知道)。

当dns查找是异步的时,正常使用通常不会有更好的性能,因为代码需要答案才能继续工作。 平行无益。 只有当你只想查找多个DNS时,这才成为一个真正的问题。

为什么它有点慢,并提高性能检查这个SO问题和答案GetHostEntry非常慢