Java中的非阻塞(异步)DNS解析

30

请问在Java中有没有一种干净的异步方式来解决DNS查询(通过主机名获取IP),以非阻塞方式进行(即状态机,而不是1个查询= 1个线程 - 我想同时运行数万个查询,但不要运行数万个线程)?

到目前为止,我找到了以下内容:

  • 标准的 InetAddress.getByName() 实现是阻塞的,似乎标准的 Java 库缺乏任何非阻塞实现。
  • 批量解析 DNS 问题讨论了类似的问题,但唯一找到的解决方案是多线程方法(即每个给定时刻只有一个线程处理一个查询),这并不真正可扩展。
  • dnsjava 库也是阻塞的。
  • 古老的非阻塞扩展 dnsjava,可以追溯到2006年,因此缺乏任何现代的Java并发技术,如Future范例使用和,遗憾的是,仅限于队列的实现。
  • dnsjnio 项目也是 dnsjava 的扩展,但它也采用线程模型(即 1 个查询 = 1 个线程)。
  • asyncorg 看起来是我目前找到的最好的解决此问题的解决方案,但:
    • 它也来自 2007 年,看起来已经被放弃
    • 缺乏几乎任何文档/javadoc
    • 使用大量非标准技术,如 Fun

还有其他我错过的想法或实现吗?

澄清。 我有大量的日志(每天几TB),每个日志行都有一个主机名,可以来自互联网上的任何地方,我需要该主机名的IP地址以进行进一步的统计计算。 行的顺序并不重要,所以,基本上,我的想法是启动2个线程:第一个迭代行:

  • 读取行,解析它,获取主机名
  • 向DNS服务器发送查询以解析给定的主机名,不要阻塞以等待答案
  • 将该行和DNS查询套接字句柄存储在内存中的某个缓冲区中
  • 转到下一行

第二个线程将:

  • 等待DNS服务器回答任何查询(使用epoll / kqueue之类的技术)
  • 读取答案,在缓冲区中找到它所属的那一行
  • 将已解析的IP与该行一起写入输出
  • 继续等待下一个答案
使用AnyEvent在Perl中实现一个简单的模型,向我展示了我的想法通常是正确的,我可以轻松地实现每秒15-20K查询的速度(天真的阻塞实现只能得到每秒2-3个查询 - 仅供比较 - 因此相差了4个数量级)。现在我需要在Java中实现同样的功能 - 我希望跳过自己编写DNS实现 ;)

4
在什么情况下需要同时进行“数万个查询”?换句话说,你真正想解决的问题是什么? - HonkyTonk
3
1个线程读取数据,将主机名封装在一个对象中并将其抛入队列,供n个线程从队列中进行阻塞DNS /获取作业,完成后将结果发送给1个线程,该线程负责排序输出。非阻塞通信可能会隐藏存在单独执行阻塞通信的线程这一事实。 - nhahtdh
1
设置本地DNS服务器也许是一个选项。即使使用Perl解决方案,速度应该更快。至少尝试连接几个DNS服务器,以提高速度并减少向它们发送请求的数量 - 这也符合您自己的利益。 - Joop Eggen
使用Java调用Perl脚本是否有限制?也许可以通过本地套接字将主机名数据提供给脚本,并从另一个本地套接字读取输出?这只是一个想法。 - Jeremy
@Jeremy:这是可能的,但这是一种有点混乱的解决方案。考虑到这个程序在一个集群化的Java环境中执行,这意味着我必须在每个集群节点上分发和维护Perl安装和所有所需的模块(如AnyEvent)。 - GreyCat
显示剩余5条评论
6个回答

5
也许你正在寻找基于MINA的Apache Directory Services DNS实现。该页面左侧边栏提供JavaDocs和其他有用指南。请点击此处查看。

谢谢,我现在去看看! - GreyCat

5

Netty正在进行非阻塞DNS的一些工作但这仍然是一个正在进行中的工作,并且可能只在5.0版本中发布。


DnsNameResolver将包含在即将发布的4.1.0版本中(目前在4.1.0.CR2中)。如果您想使用某些DNS扩展,您必须自己构建和解析协议记录,但这不应该是问题。 - Karry

3

我认为,您需要基于原始UDP使用基本套接字支持自己实现DNS客户端协议,或者使用NIO通道基于TCP实现。


2
我不知道是否有一个DNS库能够以你想要的异步模式运行,因此我无法回答你的问题。这段话太长了,不适合作为评论。
但是,你应该能够快速生成一个异步版本,而无需自己编写完整的DNS处理程序。请注意,我没有尝试过这样做,所以可能是错误的。
从dnsjava代码开始,您应该能够实现自己的解析器,提供发送方和接收方方法。查看SimpleResolver并查看send方法。您应该能够将此方法分成两个方法之一,用于发送请求,运行到TCPClient或UDPClient的调用(在此处处理实际的线路发送,如您所描述的,使用第一个线程),以及一个用于接收的方法,由第二个线程作为套接字读取的响应调用,并处理解析响应。您可能需要复制所有来自SimpleResolver的代码(需要许多私有方法,您需要许可证),或者您可以创建自己的版本并仅在类路径中加载它,或者您可以通过反射方式获得所需的方法并设置其可访问性
你可以使用NettyMina快速构建网络客户端。我更喜欢Netty的文档。
如果你选择了这条路并且能够/愿意开源它,如果你遇到麻烦,我可以抽出一些时间来帮助你。

1

-1

您有多个选择

选项1:Java 5 Executors

  1. 固定线程池:Executors.newFixedThreadPool(int)
  2. Future:Future代表异步计算的结果。提供了方法来检查计算是否完成,等待其完成以及检索计算结果。

选项2:使用MessageListener的JMS

  1. 需要依赖于JMS提供程序等。

选项3:基于Actor的框架

您可以使用这种方法很好地扩展。看看Akka


1
抱歉,我并不是在询问并行化方法 - 我已经知道哪种方法对于这样的任务最好了 - 这是事件机器的经典工作,使用最少线程。我甚至不需要一个队列 - 我处理的行的顺序并不重要,因为它们在后续处理中会被重新排序。我正在寻找可以在非阻塞模式下运行的Java DNS查询库。 - GreyCat

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