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/

4

2 回答 2

2

根据RFC for DNS Implementation,您一次只能提出一个问题,定义如下:

4.1.2. 问题部分格式

问题部分用于承载大多数查询中的“问题”,即定义所问内容的参数。该部分包含 QDCOUNT(通常为 1)条目,每个条目的格式如下:

                                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                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

在哪里:

QNAME 一个域名,表示为一系列标签,其中每个标签由一个长度八位组组成,后跟该八位组数。域名以根的空标签的零长度八位字节结束。请注意,该字段可能是奇数个八位字节;不使用填充。

QTYPE 一个两个八位字节的代码,它指定查询的类型。该字段的值包括对 TYPE 字段有效的所有代码,以及一些更通用的代码,这些代码可以匹配一种以上的 RR 类型。

Mockapetris [第 28 页] RFC 1035 域实现和规范
1987 年 11 月

QCLASS 一个两个八位字节的代码,用于指定查询的类。例如,对于 Internet,QCLASS 字段为 IN。……

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

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

编辑:我猜这个故事的寓意是 DNS 服务器不是为接受批量请求而设计的。这是有道理的,否则攻击者可能很容易从单个 DNS 服务器请求太多信息

于 2012-05-01T15:21:08.037 回答
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 服务器返回结果

于 2012-05-01T14:58:10.577 回答