批量解析DNS

3
我需要在Java中解析大量(数十万)的域名为IP地址。虽然使用InetAddress.getByName()对于小数量是可行的,但对于大数量来说速度太慢了(可能是因为它只发送一个请求到DNS服务器,并等待响应后再移动到下一个)。
是否有更有效的方法(例如批量将它们发送到DNS服务器),以减少解析大量域名所需的时间?
根据fmucar的要求,我添加了用于尝试更多线程方法的代码:
Set<String> ips = Collections.synchronizedSet(new HashSet<String>());
int i = 0;
List<Set<String>> sets = new ArrayList<Set<String>>();
for (String host : domains) {
    if (i++ % 5 == 0) {
        sets.add(new HashSet<String>());
    }
    Set<String> ipset = sets.get(sets.size()-1);
    ipset.add(host);
}
for (Set<String> ipset : sets) {
    Thread t = new Thread(new DomainResolver(ips, ipset));
    t.start();
}

每个线程的数量为250时,我们的峰值大约是每分钟700个结果。虽然比以前(<300)好了一些,但在需要解决数十万问题时仍然不太理想。将它降低到每个线程只有5个,可以大大加快速度,达到每分钟几千个。这显然会产生大量的线程,因此目前正在调查使用C进行解析,以利用 http://www.chiark.greenend.org.uk/~ian/adns/


5
最好不要将它们发送到我的DNS服务器... - Alnitak
1
递归服务器?它们将把自己的查询发送到哪里? - Alnitak
@Alnitak:看起来你是这个话题的专家 - 我可以问一下关于“批量DNS查询”的推荐方法是什么吗(我只是感兴趣)? - home
@Alnitak:明白了 :-) - home
2个回答

3
根据DNS实现的RFC,您只能按照以下定义一次性提出一个问题:

4.1.2. Question section format

The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being asked. The section contains QDCOUNT (usually 1) entries, each of the following format:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
/                     QNAME                     /
/                                               /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     QTYPE                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     QCLASS                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

where:

QNAME a domain name represented as a sequence of labels, where each label consists of a length octet followed by that number of octets. The domain name terminates with the zero length octet for the null label of the root. Note that this field may be an odd number of octets; no padding is used.

QTYPE a two octet code which specifies the type of the query. The values for this field include all codes valid for a TYPE field, together with some more general codes which can match more than one type of RR.

Mockapetris [Page 28] RFC 1035 Domain Implementation and Specification
November 1987

QCLASS a two octet code that specifies the class of the query. For example, the QCLASS field is IN for the Internet. ....

然而,你可能会得到自定义的[几乎不太可能]解析器,它们维护自己的缓存并支持批量传输,因为它们的规范略微开放。虽然我不知道是否存在这样的解析器。也许你可以编写一个:)...有关解析器的更多信息,请查看RFC的第5节。

最简单的解决方案是使用之前建议的线程。

编辑: 故事的寓意我想是DNS服务器没有设计用于接受大量请求。这是有道理的,否则攻击者可能会从单个DNS服务器请求过多的信息。


1
事实证明,尽管无法进行批量解析,但通过利用配置良好的DNS服务器可以大大加快查找速度。使用Google的DNS,我们能够每分钟处理数千个请求。 - frostmatthew

1

您可以使用java.util.concurrent.*类创建一个多线程应用程序,以执行多个查询而无需等待结果。

See ExecutorService, Runnable, Callable, Future, Thread ... classes.

如果您对此不熟悉,阅读教程可能是个好主意。

eg. You can use a `BlockingQueue`, and producer-consumer pattern.

你的应用程序的一部分将开始创建Callable对象,并在它们变得可用时将结果放入BlockingQueue中,另一部分将从BlockingQueue中获取结果并写入文件。
编辑1: 示例:
ExecutorService threadExecutor = Executors.newFixedThreadPool(50);
for(....){
  Runnable thread = new Thread(new DomainResolver(ips, ipset));
  threadExecutor.execute(thread);
}

不要一次创建和启动多个线程,而是将执行任务委托给执行器(请参见上面的编辑)服务,该服务最多接受50个线程。您需要找到最佳线程数,太多的线程意味着大部分CPU周期将用于线程之间的切换。太少意味着浪费CPU周期等待DNS服务器返回结果。


尝试了一个、四个和八个线程,但收益并不是很大。单个线程每分钟处理约275个,而八个线程每分钟处理约330个……我们真的需要更接近每分钟数千个的速度(这可能不太可能)。 - frostmatthew
如果您的软件已经达到了最优化的顶峰,那么您可能需要升级硬件。但我认为,如果之前是每分钟275个,则应该将其提高到330个以上。请展示一些代码,说明您是如何实现它的。 - fmucar
请查看我的编辑,我稍微重构了一下代码,现在它可以处理每分钟略多于700个。 - frostmatthew
增加线程数量并不意味着输出会成比例直接增加。最佳线程数取决于硬件,直接关系到每个操作的核心数和响应等待时间。您可以尝试在10到50之间使用线程,但正如我所说,这在很大程度上取决于硬件,在某些点之后可能会出现问题。 - fmucar
ExecutorService可能是最好的选择(我不得不将最大线程数增加到500),现在能够每分钟解析数万个,这使得一百万在合理的时间内可行。谢谢! - frostmatthew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接