【问题标题】:Unable to resolve DNS (sometimes?)无法解析 DNS(有时?)
【发布时间】:2014-08-12 20:21:33
【问题描述】:

假设一个应用程序一次并行请求 100 个 url 以获取 10000 个 url,我将收到以下错误,其中 50-5000 个:

无法解析远程名称'www.url.com'

我了解该错误意味着 DNS 服务器无法解析 url。但是,对于每次运行,无法解析的 url 数量会发生变化(范围从 50 到 5000)。

我是否太快提出太多请求?我什至可以这样做吗? - 在功能更强大的服务器上运行相同的测试,显示只有 10 个 url 无法解析 - 这听起来更真实。

执行并行请求的代码:

var semp = new SemaphoreSlim(100);
var uris = File.ReadAllLines(@"C:\urls.txt").Select(x => new Uri(x));

foreach(var uri in uris)
{
   Task.Run(async () =>
   {
      await semp.WaitAsync();
      var result = await Web.TryGetPage(uri); // Using HttpWebRequest
      semp.Release();
   });   
}

【问题讨论】:

  • 你能展示最基本的并行工作的代码吗?
  • @DavidHaney,完成 :-)
  • 看起来您可能只是因为并发请求而使 DNS 服务器超载,或者类似地,您的 Web 实例/类是静态的(或具有静态成员)并且所有任务都在共享连接,这将有奇怪的结果。
  • 此外,当您说在功能更强大的 Web 服务器上运行它更有效时,我敢打赌它使用的 DNS 与您的机器不同。

标签: c# windows multithreading dns


【解决方案1】:

我敢打赌你不知道 HttpWebRequest(这是所有 .net http api 的基石)的 DNS 查找是同步发生的,甚至在发出异步请求时(很烦人,对?)。这意味着一次触发许多请求会导致严重的线程池压力和大量延迟。这可能导致意外超时。如果你真的想更进一步,不要使用 .net dns 实现。您可以使用third party library 来解析主机并使用 ip 而不是主机名创建 web 请求,然后在触发请求之前手动设置主机标头。通过这种方式,您可以实现更多更高的吞吐量。

【讨论】:

  • 通过电话应答,因此代码示例不在卡片ATM上。如果这没有意义,请告诉我,当我在真正的计算机前时,我会添加代码。
  • 非常有意义。这也可以解释为什么我必须“发明”一种机制,每 10 秒调整一次 Web 请求的超时时间,以使它们不会超时。 Dns.GetHostEntryAsync 是否不足以解析主机,或者这是否也是同步的? :-)
  • 也是同步的。太糟糕了,这在我第一次注意到它几年后仍然是一个问题。
  • @ebb 如果您按照上面链接的问题中的描述设置 ThreadPool 的 minthreads 并且这可以缓解症状,那么这很可能是您的问题......否则您会在网络中超载某些东西。
  • 我试过设置ThreadPool.MinThreads(250, 250)没有效果。
【解决方案2】:

听起来您正在淹没您的本地 DNS 服务器(用行话来说,就是您的本地递归 DNS 解析器)。

当您的程序发出 DNS 解析请求时,它会向本地解析器发送一个端口 53 的数据报。该解析器通过从其缓存中回复或递归地将请求重新发送到已被识别为可能具有您正在查找的记录的其他解析器来响应。

所以,您的多线程程序导致大量数据报到处乱飞。 Internet 协议主机和路由器通过丢弃数据报包来处理拥塞和过载。这就像通过将汽车推离桥来处理桥上的交通拥堵。在过载情况下,一些数据包会消失。

因此,如果数据包丢失,则由使用数据报协议的端点软件重试。这就是 TCP 的目的,这就是它如何提供无错误数据流的假象,即使它只能与数据报通信。

因此,当您的某些 DNS 请求解析失败时,您的程序需要重试。您是数据报端点,因此您负责重试。我怀疑当您的某些请求因数据报被丢弃而超时时,.net 库会返回失败。

现在,重要的事情来了。像您这样的数据报端点程序也有责任实现拥塞控制。 TCP 使用其滑动窗口系统自动执行此操作,并使用一种称为慢启动/指数退避的算法。如果 TCP 不这样做,所有互联网路由器都会一直拥塞。这个算法是 Van Jacobson 构想出来的,你应该去看看它。

与此同时,您应该在批量 DNS 查找程序中实现它的一种简单形式。以下是您可以这样做的方法。

  1. 从批量大小开始,例如 5 次查找。
  2. 每次成功取回整个批次时,为下一批增加一个批次大小。这是慢启动。只要您没有遇到拥塞,就会增加网络负载。
  3. 每次解析名称失败时,将下一批的大小减半。因此,例如,如果您的批量大小为 30 并且您失败了,那么您的下一个批量大小将为 15。这是指数退避。您可以通过大幅减少网络负载来应对拥塞。
  4. 实施 100 左右的最大批量大小,以避免过于猪和看起来像对 DNS 系统的粗暴拒绝服务攻击。

不久前我有一个类似的项目,这个策略对我很有效。

【讨论】:

  • 感谢您的回答 :-) - 有什么办法,我可以确保它实际上是导致问题的本地 DNS 服务器?
  • 另外,为什么具有更多 CPU 和内存容量的服务器能够一次进行更多查找,从而防止出现过载情况?
猜你喜欢
  • 2010-09-18
  • 2021-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
  • 2021-07-17
  • 2022-01-24
  • 1970-01-01
相关资源
最近更新 更多