14

所以我想做很多DNS查询。

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

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

然后Task.WaitAll为一切完成。我看到ThreadPool响应我的请求的线程数急剧增加。如果我强制设置ThreadPool minThreads为 500,则工作负载的消耗速度要快得多。所有这些都指向Dns异步实现中的阻塞。

如果我Dns托管的 Dns 客户端ThreadPool替换,我可以在cpu 几乎处于空闲状态的情况下仅使用 1 或 2 个线程来消耗相同的工作负载。

问题是,Dns实现绝对是许多网络 API(、、、)的核心HttpWebRequestWebClient而且HttpClient它们似乎都受到这个问题的影响。如果我使用 3rd 方库解析 DNS,并使用 IP 地址作为 uri 中的主机发出 HTTP 请求,然后更改Host标头以修复请求,与任何涉及System.Net.Dns.

这里发生了什么?我错过了什么还是System.Net.Dns实施真的那么糟糕?

4

4 回答 4

3

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

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

于 2012-09-03T11:01:53.117 回答
2

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

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

nslookup -

>hostname1
>hostname2
...

在dos/unix下

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

该实现并不“糟糕”,它只是不是为多个批处理作业而设计的。除非有电话我也错过了。

于 2012-09-03T10:45:38.030 回答
1

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

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

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

这种限制通常有充分的理由。但是对于像 DNS 这样的数据量相对较少且服务请求成本相对较低的东西,我可以将这个限制开放到最多 100-200 个同时 DNS 请求。

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

<configuration>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="100"/>
    </connectionManagement>
  </system.net>
</configuration>

System.Net.ConnectionManagement 的 MSDN

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

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

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

于 2012-09-03T16:04:53.197 回答
0

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

为什么它有点慢,并提高性能检查这个 SO Question and answer(s) GetHostEntry is very slow

于 2012-09-03T11:20:49.747 回答