加速大批IP的反向DNS查找

时间:2014-05-29 21:12:39

标签: c# .net dns async-await reverse-dns

出于分析目的,我想对大批量的IP执行反向DNS查找。 “大”的含义,每小时至少数万。我正在寻找提高处理速度的方法,即降低每批处理时间。

Dns.GetHostEntry的异步版本包装到等待的任务中已经帮助了很多(与顺序请求相比),从而导致了appox的吞吐量。 100-200 IPs /秒:

static async Task DoReverseDnsLookups()
{
    // in reality, thousands of IPs
    var ips = new[] { "173.194.121.9", "173.252.110.27", "98.138.253.109" }; 
    var hosts = new Dictionary<string, string>();

    var tasks =
        ips.Select(
            ip =>
                Task.Factory.FromAsync(Dns.BeginGetHostEntry,
                    (Func<IAsyncResult, IPHostEntry>) Dns.EndGetHostEntry, 
                    ip, null)
                    .ContinueWith(t => 
                    hosts[ip] = ((t.Exception == null) && (t.Result != null)) 
                               ? t.Result.HostName : null));

    var start = DateTime.UtcNow;
    await Task.WhenAll(tasks);
    var end = DateTime.UtcNow;

    Console.WriteLine("Resolved {0} IPs in {1}, that's {2}/sec.", 
      ips.Count(), end - start, 
      ips.Count() / (end - start).TotalSeconds);
}

如何进一步提高处理率?

例如,有没有办法将一批IP发送到DNS服务器?

顺便说一下,我假设在封面下,异步方法使用了I/O Completion Ports - 如果我错了请纠正我。

2 个答案:

答案 0 :(得分:4)

您好以下是一些提示,以便您可以改进:

  1. 在本地缓存查询,因为此信息通常不会更改 几天甚至几年。这样您就不必每次都解决了。
  2. 大多数DNS服务器会自动缓存信息,以便下次解决 相当快。通常缓存是4小时,至少它是Windows服务器上的默认值。 这意味着如果您在短时间内批量运行此流程,它将表现得更好 如果你在白天多次解决地址,允许cahce到期。
  3. 您正在使用任务并行机构,但您仍在询问相同的DNS服务器 在您的机器上配置。我认为有两台机器使用不同的DNS服务器 改善过程。
  4. 我希望这会有所帮助。

答案 1 :(得分:1)

  • 与往常一样,我建议使用TPL DataflowActionBlock,而不是一次性触发所有请求,等待所有请求完成。使用ActionBlock MaxDegreeOfParallelismTPL可让var block = new ActionBlock<string>( async ip => { try { var host = (await Dns.GetHostEntryAsync(ip)).HostName; if (!string.IsNullOrWhitespace(host)) { hosts[ip] = host; } } catch { return; } }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5000}); 自行决定同时启动多少次调用,从而更好地利用资源:

Dns
  • 我还建议添加缓存,并确保不要多次解析同一个ip。

  • 当您使用.net的GetHostEntryAsync类时,它会在DNS旁边包含一些后备(例如LLMNR),这使得它非常慢。如果您只需要DNS查询,则可能需要使用ARSoft.Tools.Net等专用库。


P.S:关于你的代码示例的一些评论:

  1. 您应该使用FromAsync代替ConcurrentDictionary
  2. 延续可能会在不同的线程上运行,因此您应该真正使用{{1}}。